Imported Upstream version 2.6+dfsg

This commit is contained in:
Sergio Durigan Junior 2016-09-21 19:21:07 -04:00
commit a8330c488a
384 changed files with 152792 additions and 0 deletions

340
LICENSE Normal file
View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

11
MANIFEST.in Normal file
View file

@ -0,0 +1,11 @@
include LICENSE README.rst requirements.txt UPGRADING.rst
include createdb.py
recursive-include pagure *
recursive-include pagure-ci *
recursive-include files *
recursive-include milters *
recursive-include tests *
recursive-include doc *
recursive-include alembic *
recursive-include ev-server *
recursive-include webhook-server *

11
PKG-INFO Normal file
View file

@ -0,0 +1,11 @@
Metadata-Version: 1.1
Name: pagure
Version: 2.6
Summary: A light-weight git-centered forge based on pygit2..
Home-page: https://fedorahosted.org/pagure/
Author: Pierre-Yves Chibon
Author-email: pingou@pingoured.fr
License: GPLv2+
Download-URL: https://fedorahosted.org/releases/p/r/pagure/
Description: UNKNOWN
Platform: UNKNOWN

81
README.rst Normal file
View file

@ -0,0 +1,81 @@
Pagure
======
:Author: Pierre-Yves Chibon <pingou@pingoured.fr>
Pagure is a git-centered forge, python based using pygit2.
With pagure you can host your project with its documentation, let your users
report issues or request enhancements using the ticketing system and build your
community of contributors by allowing them to fork your projects and contribute
to it via the now-popular pull-request mechanism.
Homepage: https://pagure.io/pagure
See it at work: https://pagure.io
Playground version: https://stg.pagure.io
Get it running
==============
* Install the needed system libraries::
sudo dnf install git python-virtualenv libgit2-devel \
libjpeg-devel gcc libffi-devel redhat-rpm-config
.. note:: Do note the version of libgit2 that you install, for example
in ``libgit2-0.23.4-1`` you need to keep in mind the ``0.23``
* Retrieve the sources::
git clone https://pagure.io/pagure.git
cd pagure
* Install dependencies
* create the virtualenv::
virtualenv pagure_env
source ./pagure_env/bin/activate
* Install the correct version of pygit2::
pip install pygit2==<version of libgit2 found>.*
So in our example::
pip install pygit2==0.23.*
* Install the rest of the dependencies::
pip install -r requirements.txt
* Create the folder that will receive the projects, forks, docs, requests and
tickets' git repo::
mkdir {repos,docs,forks,tickets,requests}
* Create the inital database scheme::
python createdb.py
* Run it::
./runserver.py
* To get some profiling information you can also run it as::
./runserver.py --profile
This will launch the application at http://127.0.0.1:5000

218
UPGRADING.rst Normal file
View file

@ -0,0 +1,218 @@
Upgrading Pagure
================
From 2.5 to 2.6
---------------
2.6 brings quite a few changes and some of them impacting the database scheme.
Therefore when upgrading from 2.4 to 2.6, you will have to:
* Update the database schame using alembic: ``alembic upgrade head``
From 2.4 to 2.5
---------------
2.5 brings quite a few changes and some of them impacting the database scheme.
Therefore when upgrading from 2.4 to 2.5, you will have to:
* Update the database schame using alembic: ``alembic upgrade head``
From 2.3 to 2.4
---------------
2.4 brings quite a few changes and some of them impacting the database scheme.
Therefore when upgrading from 2.3.x to 2.4, you will have to:
* Update the database schame using alembic: ``alembic upgrade head``
This update also brings some new configuration keys:
* ``VIRUS_SCAN_ATTACHMENTS`` allows turning on or off checking attachments for
virus using clamav. This requires pyclamd but is entirely optional (and off by
default)
* ``PAGURE_CI_SERVICES`` allows specifying with which CI (Continuous
Integration) services this pagure instance can integrate with. Currently, only
`Jenkins` is supported, but this configuration key defaults to ``None``.
From 2.2 to 2.3
---------------
2.3 brings a few changes impacting the database scheme, including a new
`duplicate` status for tickets, a feature allowing one to `watch` or
`unwatch` a project and notifications on tickets as exist on pull-requests.
Therefore, when upgrading from 2.2.x to 2.3, you will have to :
* Create the new DB tables and the new status field using the ``createdb.py`` script.
* Update the database schame using alembic: ``alembic upgrade head``
This update also brings a new configuration key:
* ``PAGURE_ADMIN_USERS`` allows to mark some users as instance-wide admins, giving
them full access to every projects, private or not. This feature can then be
used as a way to clean spams.
* ``SMTP_PORT`` allows to specify the port to use when contacting the SMTP
server
* ``SMTP_SSL`` allows to specify whether to use SSL when contacting the SMTP
server
* ``SMTP_USERNAME`` and ``SMTP_PASSWORD`` if provided together allow to contact
an SMTP requiring authentication.
In this update is also added the script ``api_key_expire_mail.py`` meant to be
run by a daily cron job and warning users when their API token is nearing its
expiration date.
2.2.2
-----
Release 2.2.2 contains an important security fix, blocking a source of XSS
attack.
From 2.1 to 2.2
---------------
2.2 brings a number of bug fixes and a few improvements.
One of the major changes impacts the databases where we must change some of the
table so that the foreign key cascade on delete (fixes deleting a project when a
few plugins were activated).
When upgrading for 2.1 to 2.2 all you will have to do is:
* Update the database scheme using alembic: ``alembic upgrade head``
.. note:: If you run another database system than PostgreSQL the alembic
revision ``317a285e04a8_delete_hooks.py`` will require adjustment as the
foreign key constraints are named and the names are driver dependant.
From 2.0 to 2.1
---------------
2.1 brings its usual flow of improvements and bug fixes.
When upgrading from 2.0.x to 2.1 all you will have to:
* Update the database schame using alembic: ``alembic upgrade head``
From 1.x to 2.0
---------------
As the version change indicates, 2.0 brings quite a number of changes,
including some that are not backward compatible.
When upgrading to 2.0 you will have to:
* Update the database schema using alembic: ``alembic upgrade head``
* Create the new DB tables so that the new plugins work using the
``createdb.py`` script
* Move the forks git repo
Forked git repos are now located under the same folder as the regular git
repos, just under a ``forks/`` subfolder.
So the structure changes from: ::
repos/
├── foo.git
└── bar.git
forks/
├── patrick/
│  ├── test.git
│  └── ipsilon.git
└── pingou/
├── foo.git
└── bar.git
to: ::
repos/
├── foo.git
├── bar.git
└── forks/
├── patrick/
│  ├── test.git
│  └── ipsilon.git
└── pingou/
├── foo.git
└── bar.git
So the entire ``forks`` folder is moved under the ``repos`` folder where
the other repositories are, containing the sources of the projects.
Git repos for ``tickets``, ``requests`` and ``docs`` will be trickier to
move as the structure changes from: ::
tickets/
├── foo.git
├── bar.git
├── patrick/
│  ├── test.git
│  └── ipsilon.git
└── pingou/
├── foo.git
└── bar.git
to: ::
tickets/
├── foo.git
├── bar.git
└── forks/
├── patrick/
│  ├── test.git
│  └── ipsilon.git
└── pingou/
├── foo.git
└── bar.git
Same for the ``requests`` and the ``docs`` git repos.
As you can see in the ``tickets``, ``requests`` and ``docs`` folders there
are two types of folders, git repos which are folder with a name ending
with ``.git``, and folder corresponding to usernames. These last ones are
the ones to be moved into a subfolder ``forks/``.
This can be done using something like: ::
mkdir forks
for i in `ls -1 |grep -v '\.git'`; do mv $i forks/; done
* Re-generate the gitolite configuration.
This can be done via the ``Re-generate gitolite ACLs file`` button in the
admin page.
* Keep URLs backward compatible
The support of pseudo-namespace in pagure 2.0 has required some changes
to the URL schema:
https://pagure.io/pagure/053d8cc95fcd50c23a8b0a7f70e55f8d1cc7aebb
became:
https://pagure.io/pagure/c/053d8cc95fcd50c23a8b0a7f70e55f8d1cc7aebb
(Note the added /c/ in it)
We introduced a backward compatibility fix for this.
This fix is however *disabled* by default so if you wish to keep the URLs
valid, you will need to adjust you configuration file to include: ::
OLD_VIEW_COMMIT_ENABLED = True

71
alembic/env.py Normal file
View file

@ -0,0 +1,71 @@
from __future__ import with_statement
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = None
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(url=url, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
engine = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool)
connection = engine.connect()
context.configure(
connection=connection,
target_metadata=target_metadata
)
try:
with context.begin_transaction():
context.run_migrations()
finally:
connection.close()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

22
alembic/script.py.mako Normal file
View file

@ -0,0 +1,22 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision}
Create Date: ${create_date}
"""
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View file

@ -0,0 +1,45 @@
"""Adding column to store edited_by and edited_on a PR comment
Revision ID: 15ea3c2cf83d
Revises: 1cd0a853c697
Create Date: 2015-11-09 16:18:47.192088
"""
# revision identifiers, used by Alembic.
revision = '15ea3c2cf83d'
down_revision = '1cd0a853c697'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the columns editor_id and edited_on to the table
pull_request_comments.
'''
op.add_column(
'pull_request_comments',
sa.Column(
'editor_id',
sa.Integer,
sa.ForeignKey('users.id', onupdate='CASCADE'),
nullable=True)
)
op.add_column(
'pull_request_comments',
sa.Column(
'edited_on',
sa.DateTime,
nullable=True)
)
def downgrade():
''' Remove the columns editor_id and edited_on from the table
pull_request_comments.
'''
op.drop_column('pull_request_comments', 'editor_id')
op.drop_column('pull_request_comments', 'edited_on')

View file

@ -0,0 +1,29 @@
"""Add reports field to project
Revision ID: 1640c7d75e5f
Revises: 1d18843a1994
Create Date: 2016-09-09 16:11:28.099423
"""
# revision identifiers, used by Alembic.
revision = '1640c7d75e5f'
down_revision = '1d18843a1994'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column _reports to the table projects
'''
op.add_column(
'projects',
sa.Column('_reports', sa.Text, nullable=True)
)
def downgrade():
''' Drop the column _reports from the table projects.
'''
op.drop_column('projects', '_reports')

View file

@ -0,0 +1,38 @@
"""versioning_passwords
Revision ID: 1b6d7dc5600a
Revises: 3b441ef4e928
Create Date: 2016-01-13 07:57:23.465676
"""
# revision identifiers, used by Alembic.
revision = '1b6d7dc5600a'
down_revision = '3b441ef4e928'
from alembic import op
import sqlalchemy as sa
import sqlalchemy.orm
try:
from pagure.lib import model
except ImportError:
import sys
sys.path.insert(0, '.')
from pagure.lib import model
def upgrade():
engine = op.get_bind()
Session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker())
Session.configure(bind=engine)
session = Session()
for user in session.query(model.User).filter(
model.User.password != None).all():
user.password = '$1$%s' % user.password
session.add(user)
session.commit()
def downgrade():
raise ValueError("Password can not be downgraded")

View file

@ -0,0 +1,37 @@
"""Add closed_at field in PR
Revision ID: 1cd0a853c697
Revises: 6190226bed0
Create Date: 2015-10-02 09:32:15.370676
"""
# revision identifiers, used by Alembic.
revision = '1cd0a853c697'
down_revision = '6190226bed0'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column closed_at to the table pull_requests.
'''
op.add_column(
'pull_requests',
sa.Column(
'closed_at',
sa.DateTime,
nullable=True,
)
)
op.execute('''UPDATE "pull_requests" SET closed_at=date_created '''
'''WHERE STATUS != 'Open';''')
def downgrade():
''' Remove the column closed_at from the table pull_requests.
'''
op.drop_column('pull_requests', 'closed_at')

View file

@ -0,0 +1,39 @@
"""Add is_fork column to projects
Revision ID: 1d18843a1994
Revises: 32d636cb5e00
Create Date: 2016-07-17 22:02:14.495146
"""
# revision identifiers, used by Alembic.
revision = '1d18843a1994'
down_revision = '32d636cb5e00'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add is_fork column to project table'''
op.add_column(
'projects',
sa.Column(
'is_fork', sa.Boolean,
default=False,
nullable=True)
)
op.execute('''UPDATE "projects" '''
'''SET is_fork=TRUE WHERE parent_id IS NOT NULL;''')
op.execute('''UPDATE "projects" '''
'''SET is_fork=FALSE WHERE parent_id IS NULL;''')
op.alter_column(
'projects', 'is_fork',
nullable=False, existing_nullable=True)
def downgrade():
''' Revert the _is_fork column added'''
op.drop_column('projects', 'is_fork')

View file

@ -0,0 +1,29 @@
"""Add the tree_id column to PR inline comments
Revision ID: 1f3de3853a1a
Revises: 58e60d869326
Create Date: 2016-02-22 16:13:59.943083
"""
# revision identifiers, used by Alembic.
revision = '1f3de3853a1a'
down_revision = '58e60d869326'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column tree_id to the table pull_request_comments.
'''
op.add_column(
'pull_request_comments',
sa.Column('tree_id', sa.String(40), nullable=True)
)
def downgrade():
''' Remove the column tree_id from the table pull_request_comments.
'''
op.drop_column('pull_request_comments', 'tree_id')

View file

@ -0,0 +1,33 @@
"""Add notifications to tickets
Revision ID: 22db0a833d35
Revises: 317a285e04a8
Create Date: 2016-06-27 16:10:33.395495
"""
# revision identifiers, used by Alembic.
revision = '22db0a833d35'
down_revision = '317a285e04a8'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column notification to the table issue_comments.
'''
op.add_column(
'issue_comments',
sa.Column('notification', sa.Boolean, default=False, nullable=True)
)
op.execute('''UPDATE "issue_comments" SET notification=False;''')
op.alter_column(
'issue_comments', 'notification',
nullable=False, existing_nullable=True)
def downgrade():
''' Remove the column notification from the table issue_comments.
'''
op.drop_column('issue_comments', 'notification')

View file

@ -0,0 +1,46 @@
"""Add the remote_git entry
Revision ID: 257a7ce22682
Revises: 36116bb7a69b
Create Date: 2015-07-21 14:26:23.989220
"""
# revision identifiers, used by Alembic.
revision = '257a7ce22682'
down_revision = '36116bb7a69b'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column remote_git to the table pull_requests and make the
project_id_from field nullable.
'''
op.add_column(
'pull_requests',
sa.Column('remote_git', sa.Text, nullable=True)
)
op.alter_column(
'pull_requests',
column_name='project_id_from',
nullable=True,
existing_nullable=False)
op.create_check_constraint(
"ck_lcl_or_remo_pr",
"pull_requests",
'NOT(project_id_from IS NULL AND remote_git IS NULL)'
)
def downgrade():
''' Remove the column remote_git from the table pull_requests and make
the project_id_from field not nullable.
'''
op.drop_column('pull_requests', 'remote_git')
op.alter_column(
'pull_requests',
column_name='project_id_from',
nullable=False,
existing_nullable=True)

View file

@ -0,0 +1,60 @@
"""Change the status of pull_requests
Revision ID: 298891e63039
Revises: 3c25e14b855b
Create Date: 2015-06-08 13:06:11.938966
"""
# revision identifiers, used by Alembic.
revision = '298891e63039'
down_revision = '3c25e14b855b'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Adjust the status column of the pull_requests table.
'''
op.add_column(
'pull_requests',
sa.Column(
'_status', sa.Text,
sa.ForeignKey(
'status_pull_requests.status', onupdate='CASCADE'),
default='Open',
nullable=True)
)
op.execute('''UPDATE "pull_requests" '''
'''SET _status='Open' WHERE status=TRUE;''')
op.execute('''UPDATE "pull_requests" '''
'''SET _status='Merged' WHERE status=FALSE;''')
op.drop_column('pull_requests', 'status')
op.alter_column(
'pull_requests',
column_name='_status', new_column_name='status',
nullable=False, existing_nullable=True)
def downgrade():
''' Revert the status column of the pull_requests table.
'''
op.add_column(
'pull_requests',
sa.Column(
'_status', sa.Boolean, default=True, nullable=True)
)
op.execute('''UPDATE "pull_requests" '''
'''SET _status=TRUE WHERE status='Open';''')
op.execute('''UPDATE "pull_requests" '''
'''SET _status=FALSE WHERE status!='Open';''')
op.drop_column('pull_requests', 'status')
op.alter_column(
'pull_requests',
column_name='_status', new_column_name='status',
nullable=False, existing_nullable=True)

View file

@ -0,0 +1,31 @@
"""Add the milestones column
Revision ID: 2aa7b3958bc5
Revises: 443e090da188
Create Date: 2016-05-03 15:59:04.992414
"""
# revision identifiers, used by Alembic.
revision = '2aa7b3958bc5'
down_revision = '443e090da188'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column _milestones to the table projects
and the column milestone to the table issues.
'''
op.add_column(
'projects',
sa.Column('_milestones', sa.Text, nullable=True)
)
def downgrade():
''' Drop the column _milestones from the table projects
and the column milestone from the table issues.
'''
op.drop_column('projects', '_milestones')

View file

@ -0,0 +1,59 @@
"""Delete hooks
Revision ID: 317a285e04a8
Revises: 2aa7b3958bc5
Create Date: 2016-05-30 11:28:48.512577
"""
# revision identifiers, used by Alembic.
revision = '317a285e04a8'
down_revision = '2aa7b3958bc5'
from alembic import op
import sqlalchemy as sa
def upgrade():
""" Alter the hooks table to update the foreign key to cascade on delete.
"""
for table in [
'hook_fedmsg', 'hook_irc', 'hook_mail',
'hook_pagure_force_commit', 'hook_pagure', 'hook_pagure_requests',
'hook_pagure_tickets', 'hook_pagure_unsigned_commit', 'hook_rtd',
]:
op.drop_constraint(
'%s_project_id_fkey' % table,
table,
type_='foreignkey')
op. create_foreign_key(
name='%s_project_id_fkey' % table,
source_table=table,
referent_table='projects',
local_cols=['project_id'],
remote_cols=['id'],
onupdate='cascade',
ondelete='cascade',
)
op.drop_constraint(
'projects_groups_project_id_fkey',
'projects_groups',
type_='foreignkey')
op. create_foreign_key(
name='projects_groups_project_id_fkey',
source_table='projects_groups',
referent_table='projects',
local_cols=['project_id'],
remote_cols=['id'],
onupdate='cascade',
ondelete='cascade',
)
def downgrade():
""" Alter the hooks table to update the foreign key to undo the cascade
on delete.
"""

View file

@ -0,0 +1,53 @@
"""display_name_in_groups
Revision ID: 32d636cb5e00
Revises: 43df5e588a87
Create Date: 2016-08-13 02:54:27.199948
"""
# revision identifiers, used by Alembic.
revision = '32d636cb5e00'
down_revision = '43df5e588a87'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add attributes display name and description in PagureGroup '''
op.add_column(
'pagure_group',
sa.Column(
'display_name',
sa.String(255),
nullable=True,
unique=True,
)
)
op.execute('''UPDATE "pagure_group" SET display_name=group_name; ''')
op.alter_column(
'pagure_group',
column_name='display_name',
nullable=False,
existing_nullable=True
)
op.add_column(
'pagure_group',
sa.Column(
'description',
sa.String(255),
nullable=True,
)
)
def downgrade():
''' Remove attributes display name and description in PagureGroup '''
op.drop_column('pagure_group', 'display_name')
op.drop_column('pagure_group', 'description')

View file

@ -0,0 +1,61 @@
"""Add namespace to project
Revision ID: 350efb3f6baf
Revises: 1640c7d75e5f
Create Date: 2016-08-30 22:02:07.645138
"""
# revision identifiers, used by Alembic.
revision = '350efb3f6baf'
down_revision = '1640c7d75e5f'
from alembic import op
import sqlalchemy as sa
try:
from pagure.lib import model
except ImportError:
import sys
sys.path.insert(0, '.')
from pagure.lib import model
def upgrade():
''' Add the column namespace to the table projects.
'''
op.add_column(
'projects',
sa.Column('namespace', sa.String(255), nullable=True, index=True)
)
# Update all the existing projects
engine = op.get_bind()
Session = sa.orm.scoped_session(sa.orm.sessionmaker())
Session.configure(bind=engine)
session = Session()
for project in session.query(model.Project).filter(
model.Project.name.ilike('%/%')).all():
nspace, name = project.name.split('/', 1)
project.name = name
project.namespace = nspace
session.add(project)
session.commit()
def downgrade():
''' Remove the column namespace from the table projects.
'''
# Update all the existing projects
engine = op.get_bind()
Session = sa.orm.scoped_session(sa.orm.sessionmaker())
Session.configure(bind=engine)
session = Session()
for project in session.query(model.Project).filter(
model.Project.namespace != None).all():
if project.namespace.strip():
project.name = '%s/%s' % (project.namespace, project.name)
session.add(project)
session.commit()
op.drop_column('projects', 'namespace')

View file

@ -0,0 +1,29 @@
"""Add the url field to project
Revision ID: 36116bb7a69b
Revises: abc71fd60fa
Create Date: 2015-06-11 12:36:33.544046
"""
# revision identifiers, used by Alembic.
revision = '36116bb7a69b'
down_revision = 'abc71fd60fa'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column url to the table projects.
'''
op.add_column(
'projects',
sa.Column('url', sa.Text, nullable=True)
)
def downgrade():
''' Remove the column url from the table projects.
'''
op.drop_column('projects', 'url')

View file

@ -0,0 +1,29 @@
"""Add milestone to issues
Revision ID: 36386a60b3fd
Revises: 350efb3f6baf
Create Date: 2016-09-14 11:03:45.673932
"""
# revision identifiers, used by Alembic.
revision = '36386a60b3fd'
down_revision = '350efb3f6baf'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column milestone to the table issues.
'''
op.add_column(
'issues',
sa.Column('milestone', sa.String(255), nullable=True)
)
def downgrade():
''' Add the column milestone to the table issues.
'''
op.drop_column('issues', 'milestone')

View file

@ -0,0 +1,29 @@
"""Add the notifications column to projects
Revision ID: 368fd931cf7f
Revises: 36386a60b3fd
Create Date: 2016-09-18 18:51:09.625322
"""
# revision identifiers, used by Alembic.
revision = '368fd931cf7f'
down_revision = '36386a60b3fd'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column notifications to the table projects.
'''
op.add_column(
'projects',
sa.Column('_notifications', sa.String(255), nullable=True)
)
def downgrade():
''' Add the column notifications to the table projects.
'''
op.drop_column('projects', '_notifications')

View file

@ -0,0 +1,44 @@
"""Adding column to store edited_by and edited_on a issue comment
Revision ID: 3b441ef4e928
Revises: 15ea3c2cf83d
Create Date: 2015-12-03 12:34:28.316699
"""
# revision identifiers, used by Alembic.
revision = '3b441ef4e928'
down_revision = '15ea3c2cf83d'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the columns editor_id and edited_on to the table issue_comments.
'''
op.add_column(
'issue_comments',
sa.Column(
'editor_id',
sa.Integer,
sa.ForeignKey('users.id', onupdate='CASCADE'),
nullable=True)
)
op.add_column(
'issue_comments',
sa.Column(
'edited_on',
sa.DateTime,
nullable=True)
)
def downgrade():
''' Remove the columns editor_id and edited_on from the table
issue_comments.
'''
op.drop_column('issue_comments', 'editor_id')
op.drop_column('issue_comments', 'edited_on')

View file

@ -0,0 +1,29 @@
"""add an avatar email for project
Revision ID: 3c25e14b855b
Revises: b5efae6bb23
Create Date: 2015-06-08 12:05:13.832348
"""
# revision identifiers, used by Alembic.
revision = '3c25e14b855b'
down_revision = 'b5efae6bb23'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column merge_status to the table projects.
'''
op.add_column(
'projects',
sa.Column('avatar_email', sa.Text, nullable=True)
)
def downgrade():
''' Remove the column merge_status from the table projects.
'''
op.drop_column('projects', 'avatar_email')

View file

@ -0,0 +1,27 @@
"""add_closed_at_attribute_in_issues
Revision ID: 43df5e588a87
Revises: 22db0a833d35
Create Date: 2016-06-28 22:59:36.653905
"""
# revision identifiers, used by Alembic.
revision = '43df5e588a87'
down_revision = '22db0a833d35'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add closed_at column in issues table '''
op.add_column(
'issues',
sa.Column('closed_at', sa.DateTime, nullable=True)
)
def downgrade():
''' Remove the closed_at column in issues table '''
op.drop_column('issues', 'closed_at')

View file

@ -0,0 +1,32 @@
"""up to 255 characters for project.name
Revision ID: 443e090da188
Revises: 496f7a700f2e
Create Date: 2016-04-20 17:57:36.385103
"""
# revision identifiers, used by Alembic.
revision = '443e090da188'
down_revision = '496f7a700f2e'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.alter_column(
table_name='projects',
column_name='name',
type_=sa.String(255),
existing_type=sa.String(32)
)
def downgrade():
op.alter_column(
table_name='projects',
column_name='name',
type_=sa.String(32),
existing_type=sa.String(255)
)

View file

@ -0,0 +1,37 @@
"""Add priorities
Revision ID: 496f7a700f2e
Revises: 4cae55a80a42
Create Date: 2016-03-24 12:19:34.298752
"""
# revision identifiers, used by Alembic.
revision = '496f7a700f2e'
down_revision = '4cae55a80a42'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column _priorities to the table projects
and the column priority to the table issues.
'''
op.add_column(
'projects',
sa.Column('_priorities', sa.Text, nullable=True)
)
op.add_column(
'issues',
sa.Column('priority', sa.Integer, nullable=True, default=None)
)
def downgrade():
''' Drop the column _priorities from the table projects
and the column priority from the table issues.
'''
op.drop_column('projects', '_priorities')
op.drop_column('issues', 'priority')

View file

@ -0,0 +1,29 @@
"""Add the initial_comment on the PR table
Revision ID: 4cae55a80a42
Revises: 1f3de3853a1a
Create Date: 2016-03-01 12:00:34.823097
"""
# revision identifiers, used by Alembic.
revision = '4cae55a80a42'
down_revision = '1f3de3853a1a'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column initial_comment to the table pull_requests.
'''
op.add_column(
'pull_requests',
sa.Column('initial_comment', sa.Text, nullable=True)
)
def downgrade():
''' Remove the column initial_comment from the table pull_requests.
'''
op.drop_column('pull_requests', 'initial_comment')

View file

@ -0,0 +1,33 @@
"""add notification bool to PR
Revision ID: 58e60d869326
Revises: 1b6d7dc5600a
Create Date: 2016-02-12 12:39:07.839530
"""
# revision identifiers, used by Alembic.
revision = '58e60d869326'
down_revision = '1b6d7dc5600a'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column notification to the table pull_request_comments.
'''
op.add_column(
'pull_request_comments',
sa.Column('notification', sa.Boolean, default=False, nullable=True)
)
op.execute('''UPDATE "pull_request_comments" SET notification=False;''')
op.alter_column(
'pull_request_comments', 'notification',
nullable=False, existing_nullable=True)
def downgrade():
''' Remove the column notification from the table pull_request_comments.
'''
op.drop_column('pull_request_comments', 'notification')

View file

@ -0,0 +1,43 @@
"""Add the updated_on column to pull-requests
Revision ID: 6190226bed0
Revises: 257a7ce22682
Create Date: 2015-09-29 15:32:58.229183
"""
# revision identifiers, used by Alembic.
revision = '6190226bed0'
down_revision = '257a7ce22682'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column updated_on to the table pull_requests.
'''
op.add_column(
'pull_requests',
sa.Column(
'updated_on',
sa.DateTime,
nullable=True,
default=sa.func.now(),
onupdate=sa.func.now()
)
)
op.execute('''UPDATE "pull_requests" SET updated_on=date_created;''')
op.alter_column(
'pull_requests',
column_name='updated_on',
nullable=False,
existing_nullable=True)
def downgrade():
''' Remove the column updated_on from the table pull_requests.
'''
op.drop_column('pull_requests', 'updated_on')

View file

@ -0,0 +1,34 @@
"""Add the closed_by column to pull_requests
Revision ID: abc71fd60fa
Revises: 298891e63039
Create Date: 2015-06-08 16:06:18.017110
"""
# revision identifiers, used by Alembic.
revision = 'abc71fd60fa'
down_revision = '298891e63039'
from alembic import op
import sqlalchemy as sa
def upgrade():
''' Add the column merge_status to the table pull_requests.
'''
op.add_column(
'pull_requests',
sa.Column(
'closed_by_id',
sa.Integer,
sa.ForeignKey('users.id', onupdate='CASCADE'),
)
)
def downgrade():
''' Remove the column merge_status from the table pull_requests.
'''
op.drop_column('pull_requests', 'closed_by_id')

View file

@ -0,0 +1,35 @@
"""Add merge status to the pull_requests table
Revision ID: b5efae6bb23
Revises: None
Create Date: 2015-06-02 16:30:06.199128
"""
# revision identifiers, used by Alembic.
revision = 'b5efae6bb23'
down_revision = None
from alembic import op
from sqlalchemy.dialects.postgresql import ENUM
import sqlalchemy as sa
# Sources for the code: https://bitbucket.org/zzzeek/alembic/issue/67
def upgrade():
''' Add the column merge_status to the table pull_requests.
'''
enum = ENUM('NO_CHANGE', 'FFORWARD', 'CONFLICTS', 'MERGE',
name='merge_status_enum', create_type=False)
enum.create(op.get_bind(), checkfirst=False)
op.add_column(
'pull_requests',
sa.Column('merge_status', enum, nullable=True)
)
def downgrade():
''' Remove the column merge_status from the table pull_requests.
'''
ENUM(name="merge_status_enum").drop(op.get_bind(), checkfirst=False)
op.drop_column('pull_requests', 'merge_status')

14
createdb.py Normal file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env python2
# These two lines are needed to run on EL6
__requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4']
import pkg_resources
from pagure import APP
from pagure.lib import model
model.create_tables(
APP.config['DB_URL'],
APP.config.get('PATH_ALEMBIC_INI', None),
acls=APP.config.get('ACLS', {}),
debug=True)

153
doc/Makefile Normal file
View file

@ -0,0 +1,153 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pagure.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pagure.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/pagure"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pagure"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

BIN
doc/_static/overview.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
doc/_static/overview_simple.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
doc/_static/pagure.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

16
doc/_static/site.css vendored Normal file
View file

@ -0,0 +1,16 @@
@import url("cloud.css");
@import url("http://fonts.googleapis.com/css?family=Comfortaa");
.pagure-logo span {
background: url("pagure.png") no-repeat scroll 50% 0 transparent;
display: block;
width: 134px;
height: 64px;
margin-bottom: 10px;
}
h1.pagure-logo {
font-family: 'Comfortaa', sans-serif;
margin-top: -10px;
margin-bottom: 12px;
}

1
doc/_templates/pagure-logo.html vendored Normal file
View file

@ -0,0 +1 @@
<h1 class='pagure-logo'><span></span>Pagure</h1>

5
doc/api.rst Normal file
View file

@ -0,0 +1,5 @@
Pagure API
==========
The API documentation can be found at https://pagure.io/api/0/ or within
the sources in ``pagure/doc/api.rst``.

317
doc/conf.py Normal file
View file

@ -0,0 +1,317 @@
# -*- coding: utf-8 -*-
#
# pagure documentation build configuration file, created by
# sphinx-quickstart on Wed Mar 1 10:30:13 2015.
#
# This file is execfile()d with the current directory set to its containing
# dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import os
import re
pagurefile = os.path.join(
os.path.dirname(__file__), '..', 'pagure', '__init__.py')
# Thanks to SQLAlchemy:
# https://github.com/zzzeek/sqlalchemy/blob/master/setup.py#L104
with open(pagurefile) as stream:
VERSION = re.compile(
r".*__version__ = '(.*?)'", re.S
).match(stream.read()).group(1)
# If extensions (or modules to document with autodoc) are in another
# directory, add these directories to sys.path here. If the directory is
# relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'pagure'
copyright = u'2015, Red Hat Inc, Pierre-Yves Chibon <pingou@pingoured.fr>'
# The version info for the project you're documenting, acts as replacement
# for |version| and |release|, also used in various other places throughout
# the built documents.
#
# The short X.Y version.
#version = __version__
version = VERSION
# The full version, including alpha/beta/rc tags.
#release = '1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ----------------------------------------------
import cloud_sptheme as csp
html_style = 'site.css'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#html_theme = 'default'
html_theme = "cloud"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
html_theme_options = {
"sidebarwidth": "200px",
"max_width": "900px",
"compact_width": "800px",
"minimal_width": "700px",
# Style it like Fedora..
"bodyfont": "Cantarell",
"highlightcolor": "#79db32", # First Green
"sidebarbgcolor": "#FEFEFE",
"sidebartrimcolor": "#FEFEFE",
"sectionbgcolor": "#FEFEFE",
"sectiontrimcolor": "#FEFEFE",
"sectiontextcolor": "#444444",
"relbarbgcolor": "#FEFEFE",
"relbartextcolor": "#444444",
"relbarlinkcolor": "#444444",
"bgcolor": "#FEFEFE",
"textcolor": "#444444",
#"linkcolor": "#79db32", # First Green
"linkcolor": "#00009d",
"headtextcolor": "#444444",
"headlinkcolor": "#444444",
#"codebgcolor"
#"codetextcolor"
"codetrimcolor": "#79db32", # First Green
"footerbgcolor": "#FEFEFE",
}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
html_theme_path = [csp.get_theme_dir()]
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': [
'pagure-logo.html',
'localtoc.html',
'relations.html',
'sourcelink.html',
'searchbox.html',
]
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'pagure'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
(
'index', 'pagure.tex', u'Pagure Documentation',
u'Pierre-Yves Chibon \\textless{}pingou@pingoured.fr\\textgreater{}',
'manual'
),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(
'index', 'pagure', u'Pagure Documentation',
[u'Pierre-Yves Chibon <pingou@pingoured.fr>'],
1
)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
'index', 'pagure', u'Pagure Documentation',
u'Pierre-Yves Chibon <pingou@pingoured.fr>', 'pagure',
'Small git-centric forge',
'Miscellaneous'
),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

532
doc/configuration.rst Normal file
View file

@ -0,0 +1,532 @@
Configuration
=============
Pagure offers a wide varieties of options that must or can be used to
adjust its behavior.
Must options
------------
Here are the options you must set up in order to get pagure running.
SECRET_KEY
~~~~~~~~~~
This key is used by flask to create the session. It should be kept secret
and set as a long and random string.
SALT_EMAIL
~~~~~~~~~~
This key is used for when sending notification to ensure that when sending
notifications to different users, each one of them has a different, unique
and un-fakable ``Reply-To`` header that is then used by the milter to find
out if the response received is a real one or a fake/invalid one.
DB_URL
~~~~~~
This key indicates to the framework how and where to connect to the database
server. Pagure using `SQLAchemy <http://www.sqlalchemy.org/>`_ it can connect
to a wide range of database server including MySQL, PostgreSQL and SQLite.
Examples values:
::
DB_URL=mysql://user:pass@host/db_name
DB_URL=postgres://user:pass@host/db_name
DB_URL = 'sqlite:////var/tmp/pagure_dev.sqlite'
Defaults to ``sqlite:////var/tmp/pagure_dev.sqlite``
APP_URL
~~~~~~~
This key indicates the URL at which this pagure instance will be made available.
Defaults to: ``https://pagure.org/``
EMAIL_ERROR
~~~~~~~~~~~
Pagure sends email when it caches an un-expected error (which saves you from
having to monitor the logs regularly but if you like, the error is still
present in the logs).
This setting allows you to specify to which email address to send these error
reports.
GIT_URL_SSH
~~~~~~~~~~~
This configuration key provides the information to the user on how to clone
the git repos hosted on pagure via `SSH <https://en.wikipedia.org/wiki/Secure_Shell>`_.
The URL should end with a slash ``/``.
Defaults to: ``'ssh://git@pagure.org/'``
GIT_URL_GIT
~~~~~~~~~~~
This configuration key provides the information to the user on how to clone
the git repos hosted on pagure anonymously. This access can be granted via
the ``git://`` or ``http(s)://`` protocols.
The URL should end with a slash ``/``.
Defaults to: ``'git://pagure.org/'``
GIT_FOLDER
~~~~~~~~~~
This configuration key points to where the folders containing the git repos
of the projects are located.
Each project in pagure has 4 git repositories:
- the main repo for the code
- the doc repo showed in the doc server
- the ticket and request repos storing the metadata of the
tickets/pull-requests
There are then another 2 folders specifying the locations of the forks and
remote git repo used for the remotes pull-requests (ie: pull-request coming
from a project not hosted on this instance of pagure).
FORK_FOLDER
~~~~~~~~~~~
This configuration key points to the folder where the git repos of forks of
the projects are stored.
DOCS_FOLDER
~~~~~~~~~~~
This configuration key points to the folder where the git repos for the
documentation of the projects are stored.
TICKETS_FOLDER
~~~~~~~~~~~~~~
This configuration key points to the folder where the git repos storing the
metadata of the tickets opened against the project are stored .
REQUESTS_FOLDER
~~~~~~~~~~~~~~~
This configuration key points to the folder where the git repos storing the
metadata of the pull-requests opened against the project are stored.
REMOTE_GIT_FOLDER
~~~~~~~~~~~~~~~~~
This configuration key points to the folder where the remote git repos (ie:
not hosted on pagure) that someone used to open a pull-request against a
project hosted on pagure are stored.
SESSION_COOKIE_SECURE
~~~~~~~~~~~~~~~~~~~~~
When this is set to True, the session cookie will only be returned to the
server via ssl (https). If you connect to the server via plain http, the
cookie will not be sent. This prevents sniffing of the cookie contents.
This may be set to False when testing your application but should always
be set to True in production.
Defaults to: ``False`` for development, must be ``True`` in production with
https.
FROM_EMAIL
~~~~~~~~~~
This setting allows one to specify the email address used by this pagure instance
when sending emails (notifications).
Defaults to: ``pagure@pagure.org``
DOMAIN_EMAIL_NOTIFICATIONS
~~~~~~~~~~~~~~~~~~~~~~~~~~
This setting allows one to specify the domain used by this pagure instance
when sending emails (notifications). More precisely, this setting is used
when building the ``msg-id`` header of the emails sent.
Defaults to: ``pagure.org``
VIRUS_SCAN_ATTACHMENTS
~~~~~~~~~~~~~~~~~~~~~~
This setting configures whether attachments are scanned for viruses on
upload. For more information, see the install.rst guide.
Defaults to: ``False``
Configure Gitolite
------------------
Pagure uses `gitolite <http://gitolite.com/>`_ as an authorization layer.
Gitolite relies on `SSH <https://en.wikipedia.org/wiki/Secure_Shell>`_ for
the authentication. In other words, SSH let you in and gitolite check if you
are allowed to do what you are trying to do once you are inside.
GITOLITE_HOME
~~~~~~~~~~~~~
This configuration key should point to the home of the user under which
gitolite is ran.
GITOLITE_VERSION
~~~~~~~~~~~~~~~~
This configuration key allows one to specify which version of gitolite you are
using, it can be either ``2`` or ``3``.
Defaults to: ``3``.
GITOLITE_KEYDIR
~~~~~~~~~~~~~~~
This configuration key points to the folder where gitolite stores and accesses
the public SSH keys of all the user have access to the server.
Since pagure is the user interface, it is pagure that writes down the files
in this directory effectively setting up the users to be able to use gitolite.
GL_RC
~~~~~
This configuration key must point to the file ``gitolite.rc`` used by gitolite
to record who has access to what (ie: who has access to which repo/branch).
GL_BINDIR
~~~~~~~~~
This configuration key indicates the folder in which the gitolite tools can
be found. It can be as simple as ``/usr/bin/`` if the tools have been installed
using a package manager or something like ``/opt/bin/`` for a more custom
install.
EventSource options
-------------------
EVENTSOURCE_SOURCE
~~~~~~~~~~~~~~~~~~
This configuration key indicates the URL at which the EventSource server is
available. If not defined, pagure will behave as if there are no EventSource
server running.
EVENTSOURCE_PORT
~~~~~~~~~~~~~~~~
This configuration key indicates the port at which the EventSource server is
running. This allows adjusting the port via the configuration file instead
of hard-coding it in the code.
.. note:: The EventSource server requires a redis server (see ``Redis options``
below)
Web-hooks notifications
-----------------------
WEBHOOK
~~~~~~~
This configuration key allows turning on or off web-hooks notifications for
this pagure instance.
Defaults to: ``False``.
.. note:: The Web-hooks server requires a redis server (see ``Redis options``
below)
Redis options
-------------
REDIS_HOST
~~~~~~~~~~
This configuration key indicates the host at which the `redis <http://redis.io/>`_
server is running.
Defaults to: ``0.0.0.0``.
REDIS_PORT
~~~~~~~~~~
This configuration key indicates the port at which the reds server can be
contacted.
Defaults to: ``6379``.
REDIS_DB
~~~~~~~~
This configuration key indicates the name of the redis database to use to
communicate with the EventSource server.
Defaults to: ``0``.
Authentication options
----------------------
ADMIN_GROUP
~~~~~~~~~~~
List of groups, local or remotes (if the openid server used supports the
group extension), that are site admin. These admins can regenerate the
gitolite configuration, the ssh key files, the hook-token for every project
as well as manage users and groups.
PAGURE_ADMIN_USERS
~~~~~~~~~~~~~~~~~~
List of usernames that are site admin. These admins have the same rights as
the user in the admin groups (listed above) as well as admin rights to
every projects hosted on this pagure instance.
Optional options
----------------
SSH_KEYS
~~~~~~~~
It is a good pratice to publish the fingerprint and public SSH key of a
server you provide access to.
Pagure offers the possibility to expose this information based on the values
set in the configuration file, in the ``SSH_KEYS`` configuration key.
See the `SSH hostkeys/Fingerprints page on pagure.io <https://pagure.io/ssh_info>`_.
.. warning: The format is important
SSH_KEYS = {'RSA': {'fingerprint': '<foo>', 'pubkey': '<bar>'}}
Where `<foo>` and `<bar>` must be replaced by your values.
ITEM_PER_PAGE
~~~~~~~~~~~~~
This configuration key allows you to configure the length of a page by
setting the number of items on the page. Items can be commits, users, groups
or projects for example.
Defaults to: ``50``.
SMTP_SERVER
~~~~~~~~~~~
This configuration key allows one to configure the SMTP server to use when
sending emails.
Defaults to: ``localhost``.
SMTP_PORT
~~~~~~~~~
This configuration key allows one to define the SMTP server port.
SMTP by default uses TCP port 25. The protocol for mail submission is
the same, but uses port 587.
SMTP connections secured by SSL, known as SMTPS, default to port 465
(nonstandard, but sometimes used for legacy reasons).
Defaults to: ``25``
SMTP_SSL
~~~~~~~~
This configuration key allows one to specify whether the SMTP connections
should secured over SSL
Defaults to: ``False``
SMTP_USERNAME
~~~~~~~~~~~~~
This configuration key allows usage of SMTP with auth
Note: Specify SMTP_USERNAME and SMTP_PASSWORD for using SMTP auth
Defaults to: ``None``
SMTP_PASSWORD
~~~~~~~~~~~~~
This configuration key allows usage of SMTP with auth
Note: Specify SMTP_USERNAME and SMTP_PASSWORD for using SMTP auth
Defaults to: ``None``
SHORT_LENGTH
~~~~~~~~~~~~
This configuration key allows one to configure the length of the commit ids or
file hex displayed in the user interface.
Defaults to: ``6``.
BLACKLISTED_PROJECTS
~~~~~~~~~~~~~~~~~~~~
This configuration key allows one to set a list of project name that are forbidden.
This list is used for example to avoid conflicts at the URL level between the
static files located under ``/static/`` and a project that would be named
``static`` and thus be located at ``/static``.
Defaults to:
::
[
'static', 'pv', 'releases', 'new', 'api', 'settings',
'logout', 'login', 'users', 'groups'
]
CHECK_SESSION_IP
~~~~~~~~~~~~~~~~
This configuration key allows one to configure whether to check the user's IP
address when retrieving its session. This makes things more secure but
under certain setup it might not work (for example if there are proxies
in front of the application).
Defaults to: ``True``.
PAGURE_AUTH
~~~~~~~~~~~~
This configuration key allows one to specify which authentication method to use.
Pagure supports currently two authentication methods, one relying on the
Fedora Account System `FAS <https://admin.fedoraproject.org/accounts>`_,
the other relying on local user accounts.
It can therefore be either ``fas`` or ``local``.
Defaults to: ``fas``.
IP_ALLOWED_INTERNAL
~~~~~~~~~~~~~~~~~~~
This configuration key allows one to specify which IP addresses are allowed
to access the internal API endpoint. These endpoints are accessed by the
milters for example and allow one to perform action in the name of someone else.
So they are sensitive, thus the check for the origin of the request using
these endpoints.
Defaults to: ``['127.0.0.1', 'localhost', '::1']``.
MAX_CONTENT_LENGTH
~~~~~~~~~~~~~~~~~~
This configuration key allows one to specify the maximum size allowed when
uploading content to pagure (for example, screenshots to a ticket).
Defaults to: ``4 * 1024 * 1024`` which corresponds to 4 megabytes.
ENABLE_TICKETS
~~~~~~~~~~~~~~
This configuration key allows one to activate or de-activate the ticketing system
for all the projects hosted on this pagure instance.
Defaults to: ``True``
ENABLE_NEW_PROJECTS
~~~~~~~~~~~~~~~~~~~
This configuration key allows one to create or forbids creating new projects in
the user interface of this pagure instance.
Defaults to: ``True``
ENABLE_DEL_PROJECTS
~~~~~~~~~~~~~~~~~~~
This configuration key allows one to delete or forbids deleting projects in
the user interface of this pagure instance.
Defaults to: ``True``
EMAIL_SEND
~~~~~~~~~~
This configuration key allows turning on or off all email notification for
this pagure instance. This can be useful to turn off when developing on
pagure, or for test or pre-production instances.
Defaults to: ``True``.
OLD_VIEW_COMMIT_ENABLED
~~~~~~~~~~~~~~~~~~~~~~~
In version 1.3, pagure changed its URL scheme to view the commit of a
project in order to add support for pseudo-namespaced projects.
For pagure instances older than 1.3, who care about backward compatibility,
we added an endpoint ``view_commit_old`` that brings URL backward
compatibility for URLs using the complete git hash (the 40 characters).
For URLs using a shorter hash, the URLs will remain broken.
This configuration key allows turning on or off this backward compatibility
which is useful for pagure instances running since before 1.3 but is not
for newer instances.
Defaults to: ``False``.
PAGURE_CI_SERVICES
~~~~~~~~~~~~~~~~~~
Pagure can be configure to integrate results of a Continuous Integration (CI)
service to pull-requests open against a project.
To enable this integration, follow the documentation on how to install
pagure-ci and set this configuration key to ``['jenkins']`` (Jenkins being
the only CI service supported at the moment).
Defaults to: ``None``.
.. warning:: Requires `Redis` to be configured and running.

27
doc/contributing.rst Normal file
View file

@ -0,0 +1,27 @@
Contributing
============
If you're submitting patches to pagure, please observe the following:
- Check that your python code is `PEP8-compliant
<http://www.python.org/dev/peps/pep-0008/>`_. There is a `pep8 tool
<http://pypi.python.org/pypi/pep8>`_ that can automatically check
your source.
- Check that your code doesn't break the test suite. The test suite can be
run using the ``runtests.sh`` shell script at the top of the sources.
See :doc:`development` for more information about the test suite.
- If you are adding new code, please write tests for them in ``tests/``,
the ``runtests.sh`` script will help you to see the coverage of your code
in unit-tests.
- If your change warrants a modification to the docs in ``doc/`` or any
docstrings in ``pagure/`` please make that modification.
.. note:: You have a doubt, you don't know how to do something, you have an
idea but don't know how to implement it, you just have something bugging
you?
Come to see us on IRC: ``#fedora-apps`` on irc.freenode.net or directly on
`the project <http://pagure.io>`_.

85
doc/contributors.rst Normal file
View file

@ -0,0 +1,85 @@
Contributors to pagure
=========================
Pagure would be nothing without its contributors.
On Sep 20, 2016 (release 2.6), the list looks as follow:
================= ===========
Number of commits Contributor
================= ===========
4400 Pierre-Yves Chibon <pingou@pingoured.fr>
186 Ryan Lerch <rlerch@redhat.com>
89 farhaanbukhsh <farhaan.bukhsh@gmail.com>
59 Johan Cwiklinski <johan@x-tnd.be>
51 Clement Verna <cverna@tutanota.com>
50 Vivek Anand <vivekanand1101@gmail.com>
27 Farhaan Bukhsh <farhaan.bukhsh@gmail.com>
18 Sayan Chowdhury <sayan.chowdhury2012@gmail.com>
17 Lubomír Sedlář <lsedlar@redhat.com>
17 Patrick Uiterwijk <puiterwijk@redhat.com>
15 Gaurav Kumar <aavrug@gmail.com>
15 Ralph Bean <rbean@redhat.com>
13 Ghost-script <subho.prp@gmail.com>
13 Mathieu Bridon <bochecha@fedoraproject.org>
8 Lei Yang <yltt1234512@gmail.com>
5 Mike McLean <mikem@redhat.com>
5 Oliver Gutierrez <ogutierrez@redhat.com>
5 Paul W. Frields <stickster@gmail.com>
5 vanzhiganov <vanzhiganov@ya.ru>
5 yangl1996 <yltt1234512@gmail.com>
4 Eric Barbour <ebarbour@redhat.com>
4 Maciej Lasyk <maciek@lasyk.info>
3 Ankush Behl <cloudbehl@gmail.com>
3 Anthony Lackey <alackey96@gmail.com>
3 Dhriti Shikhar <dhriti.shikhar.rokz@gmail.com>
3 Jan Pokorný <jpokorny@redhat.com>
3 Jason Tibbitts <tibbs@math.uh.edu>
3 Kushal Khandelwal <kushal124@gmail.com>
3 Pedro Lima <pedro.lima@gmail.com>
3 Sergio Durigan Junior <sergiodj@sergiodj.net>
3 skrzepto <shims506@gmail.com>
2 Daniel Mach <dmach@redhat.com>
2 Nuno Maltez <nuno@cognitiva.com>
2 Richard Marko <rmarko@fedoraproject.org>
2 Ricky Elrod <ricky@elrod.me>
2 Simo Sorce <simo@redhat.com>
2 Till Maas <opensource@till.name>
2 William Moreno Reyes <williamjmorenor@gmail.com>
2 bruno <bruno@wolff.to>
2 dhrish20 <dhrish20@gmail.com>
2 tenstormavi <avi.avinash3008@gmail.com>
1 Aleksandra Fedorova (bookwar) <afedorova@mirantis.com>
1 Anthony Lackey <alackey@localhost.localdomain>
1 David Caro <dcaroest@redhat.com>
1 Eric Barbour <emb4gu@virginia.edu>
1 Haikel Guemar <hguemar@fedoraproject.org>
1 Jeremy Cline <jeremy@jcline.org>
1 Kunaal Jain <kunaalus@gmail.com>
1 Mathew Robinson <mathew.robinson3114@gmail.com>
1 Pierre-YvesChibon <pingou@fedoraproject.org>
1 Rahul Bajaj <rahulrb0509@gmail.com>
1 Stanislav Ochotnicky <sochotnicky@redhat.com>
1 Tiago M. Vieira <tiago@tvieira.com>
1 Vyacheslav Anzhiganov <vanzhiganov@ya.ru>
1 Yves Martin <ymartin1040@gmail.com>
1 abhishek <abhishekarora12@gmail.com>
1 abhishek goswami <abhishekg785@gmail.com>
1 jcvicelli <jcvicelli@gmail.com>
1 pingou <pingou@fedoraproject.org>
1 ryanlerch <rlerch@redhat.com>
1 skrzepto <skrzepto@gmail.com>
1 “AnjaliPardeshi” <“anjalipardeshi92@gmail.com”>
================= ===========
This list is generated using
::
git shortlog -s -n -e
The old pagure logo has been created by ``Micah Denn <micah.denn@gmail.com>``,
the new one, as well as the entire version 2 of the user interface (using
bootstrap) is the work of ``Ryan Lerch <rlerch@redhat.com>`` many thanks
to them for their work and understanding during the process.

261
doc/development.rst Normal file
View file

@ -0,0 +1,261 @@
Development
===========
Get the sources
---------------
Anonymous:
::
git clone https://pagure.io/pagure.git
Contributors:
::
git clone ssh://git@pagure.io:pagure.git
Dependencies
------------
The dependencies of pagure are listed in the file ``requirements.txt``
at the top level of the sources.
.. note:: working in a `virtualenv <http://www.virtualenv.org/en/latest/>`_
is tricky due to the dependency on `pygit2 <http://www.pygit2.org/>`_
and thus on `libgit2 <https://libgit2.github.com/>`_
but the pygit2 `documentation has a solution for this
<http://www.pygit2.org/install.html#libgit2-within-a-virtual-environment>`_.
Run pagure for development
--------------------------
Adjust the configuration file (secret key, database URL, admin group...)
See :doc:`configuration` for more detailed information about the
configuration.
Create the database scheme::
./createdb
Create the folder that will receive the different git repositories:
::
mkdir {repos,docs,forks,tickets,requests,remotes}
Run the server:
::
./runserver
If you want to change some configuration key you can create a file, place
the configuration change in it and use it with
::
./runserver -c <config_file>
For example, create the file ``config`` with in it:
::
from datetime import timedelta
# Makes the admin session longer
ADMIN_SESSION_LIFETIME = timedelta(minutes=20000000)
# Use a postgresql database instead of sqlite
DB_URL = 'postgresql://user:pass@localhost/pagure'
# Change the OpenID endpoint
FAS_OPENID_ENDPOINT = 'https://id.stg.fedoraproject.org'
APP_URL = '*'
EVENTSOURCE_SOURCE = 'http://localhost:8080'
EVENTSOURCE_PORT = '8080'
DOC_APP_URL = '*'
# Avoid sending email when developping
EMAIL_SEND = False
and run the server with:
::
./runserver -c config
To get some profiling information you can also run it as:
::
./runserver.py --profile
You should be able to access the server at http://localhost:5000
Every time you save a file, the project will be automatically restarted
so you can see your change immediately.
Create a pull-request for testing
----------------------------------
When working on pagure, it is pretty often that one wanted to work on a
feature or a bug related to pull-requests needs to create one.
Making a pull-request for development purposes isn't hard, if you remember
that since you're running a local instance, the git repos created in your
pagure instance are also local.
So here are in a few steps that one could perform to create a pull-request in a
local pagure instance.
* Create a project on your pagure instance, let's say it will be called ``test``
* Create a folder ``clones`` somewhere in your system (you probably do not
want it in the ``repos`` folder created above, next to it is fine though)::
mkdir clones
* Clone the repo of the ``test`` project into this ``clones`` folder::
cd clones
git clone ~/path/to/pagure/repos/test.git
* Add and commit some files::
echo "*~" > .gitignore
git add .gitignore
git commit -m "Add a .gitignore file"
echo "BSD" > LICENSE
git add LICENSE
git commit -m "Add a LICENSE file"
* Push these changes::
git push -u origin master
* Create a new branch and add a commit in it::
git branch new_branch
git checkout new_branch
touch test
git add test
git commit -m "Add file: test"
* Push this new branch::
git push -u origin new_branch
Then go back to your pagure instance running in your web-browser, check the
``test`` project. You should see two branches: ``master`` and ``new_branch``
from there you should be able to open a new pull-request, either from the
front page or via the ``File Pull Request`` button in the ``Pull Requests``
page.
Coding standards
----------------
We are trying to make the code `PEP8-compliant
<http://www.python.org/dev/peps/pep-0008/>`_. There is a `pep8 tool
<http://pypi.python.org/pypi/pep8>`_ that can automatically check
your source.
We are also inspecting the code using `pylint
<http://pypi.python.org/pypi/pylint>`_ and aim of course for a 10/10 code
(but it is an assymptotic goal).
.. note:: both pep8 and pylint are available in Fedora via yum:
::
yum install python-pep8 pylint
Send patch
----------
The easiest way to work on pagure is to make your own branch in git, make
your changes to this branch, commit whenever you want, rebase on master,
whenever you need and when you are done, send the patch either by email,
via the trac or a pull-request (using git or github).
The workflow would therefore be something like:
::
git branch <my_shiny_feature>
git checkout <my_shiny_feature>
<work>
git commit file1 file2
<more work>
git commit file3 file4
git checkout master
git pull
git checkout <my_shiny_feature>
git rebase master
git format-patch -2
This will create two patch files that you can send by email to submit in a ticket
on pagure, by email or after forking the project on pagure by submitting a
pull-request (in which case the last step above ``git format-patch -2`` is not
needed.
Unit-tests
----------
Pagure has a number of unit-tests.
We aim at having a full (100%) coverage of the whole code (including the
Flask application) and of course a smart coverage as in we want to check
that the functions work the way we want but also that they fail when we
expect it and the way we expect it.
Tests checking that function are failing when/how we want are as important
as tests checking they work the way they are intended to.
``runtests.sh``, located at the top of the sources, helps to run the
unit-tests of the project with coverage information using `python-nose
<https://nose.readthedocs.org/>`_.
.. note:: You can specify additional arguments to the nose command used
in this script by just passing arguments to the script.
For example you can specify the ``-x`` / ``--stop`` argument:
`Stop running tests after the first error or failure` by just doing
::
./runtests.sh --stop
Each unit-tests files (located under ``tests/``) can be called
by alone, allowing easier debugging of the tests. For example:
::
python tests/test_pragure_lib.py
.. note:: In order to have coverage information you might have to install
``python-coverage``
::
yum install python-coverage

52
doc/index.rst Normal file
View file

@ -0,0 +1,52 @@
Pagure
=======
Pagure is a light-weight git-centered forge based on pygit2.
Features:
* ``Open-sources``: Web-interface for the git repositories
* ``Flexibility``: Ability to create any project you want
* ``One place``: Keep your documentation and tickets in pagure
* ``Collaboration``: Fork a project and make a pull-request
* ``Integration``: Create pull-request from a fork hosted somewhere else than in
pagure
* ``Open data``: Sources, doc, ticket and pull-requests meta-data are available
in the web interface but also in git repos which can thus be cloned and changed
locally.
* ``Freedom``: Pagure is fully Free and Open-Source Software!
Resources:
- `Home page <https://pagure.io/>`_
- `Git repository <http://pagure.io/pagure>`_
- `Github mirror <https://github.com/pypingou/pagure>`_
Contents:
.. toctree::
:maxdepth: 2
overview
install
install_milter
install_evs
install_webhooks
install_pagure_ci
configuration
development
usage
contributing
contributors
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

282
doc/install.rst Normal file
View file

@ -0,0 +1,282 @@
Installing pagure
=================
There are two ways to install pagure:
* via the RPM package (recommended if you are using a RPM-based linux distribution)
* via the setup.py
Installing pagure via RPM
-------------------------
Here as well there are two ways of obtaining the RPM:
* From the main repositories
Pagure is packaged for Fedora since Fedora 21 and is available for RHEL and
its derivative via the `EPEL repository <https://fedoraproject.org/wiki/EPEL>`.
So installing it is as easy as:
::
dnf install pagure pagure-milters pagure-ev pagure-webhook
or
::
yum install pagure pagure-milters pagure-ev pagure-webhook
The ``pagure`` package contains the core of the application and the doc server.
(See the ``Overview`` page for a global overview of the structure of the
project).
The ``pagure-milters`` package contains, as the name says, the milter (a
mail filter to hook into a MTA).
The ``pagure-ev`` package contains the eventsource server.
The ``pagure-webhook`` package contains the web-hook server.
.. note:: The last three packages are optional, pagure would work fine without
them but the live-update, the webhook and the comment by email
services will not work.
* From the sources
If you wish to run a newer version of pagure than what is in the repositories
you can easily rebuild it as RPM.
Simply follow these steps:
# Clone the sources::
git clone https://pagure.io/pagure.git
# Go to the folder::
cd pagure
# Build a tarball of the latest version of pagure::
python setup.py sdist
# Build the RPM::
rpmbuild -ta dist/pagure*.tar.gz
This will build pagure from the version present in your clone.
Once, the RPM is installed the services ``pagure_milter`` and ``pagure_ev``
are ready to be used but the database and the web-application parts still
need to be configured.
Installing pagure via setup.py
------------------------------
Pagure includes in its sources a ``setup.py`` automatint the installation
of the web applications of pagure (ie: the core + the doc server).
To install pagure via this mechanism simply follow these steps:
# Clone the sources::
git clone https://pagure.io/pagure.git
# Go to the folder::
cd pagure
# Install the latest version of pagure::
python setup.py build
sudo python setup.py install
.. note:: To install the eventsource server or the milter, refer to their
respective documentations.
# Install the additional files as follow:
+------------------------------+------------------------------------------+
| Source | Destination |
+=============================+===========================================+
| ``files/pagure.cfg.sample`` | ``/etc/pagure/pagure.cfg`` |
+------------------------------+------------------------------------------+
| ``files/alembic.ini`` | ``/etc/pagure/alembic.ini`` |
+------------------------------+------------------------------------------+
| ``files/pagure.conf`` | ``/etc/httpd/conf.d/pagure.conf`` |
+------------------------------+------------------------------------------+
| ``files/pagure.wsgi`` | ``/usr/share/pagure/pagure.wsgi`` |
+------------------------------+------------------------------------------+
| ``createdb.py`` | ``/usr/share/pagure/pagure_createdb.py`` |
+------------------------------+------------------------------------------+
Set-up pagure
-------------
Once pagure's files are installed, you still need to set up some things.
* Create the folder release
This folder is used by project maintainers to upload the tarball of the
releases of their project.
This folder must be accessible by the user under which the application is
running (in our case: ``git``).
::
mkdir -p /var/www/releases
chown git:git /var/www/releases
* Create the folders where the repos, forks and checkouts will be stored
Pagure stores the sources of a project in a git repo, offers a place to
store the project's documentation in another repo, stores a JSON dump of all
issues and of all pull-requests in another two repos, and keeps a local
checkout of remote projects when asked to do remote pull-requests.
All these repositories are stored in different folders that must be
created manually.
For example you can place them under ``/srv/git/repositories/`` which would
make ``/srv/git`` the home of your gitolite user.
You would then create the folders with:
::
mkdir /srv/git/repositories/{docs,forks,tickets,requests,remotes}
* Configure apache
If installed by RPM, you will find an example apache configuration file
at: ``/etc/httpd/conf.d/pagure.conf``.
If not installed by RPM, the example files is present in the sources at:
``files/pagure.conf``.
Adjust it for your needs.
* Configure the WSGI file
If you installed by RPM, you will find an example WSGI file at:
``/usr/share/pagure/pagure.wsgi`` and ``/usr/share/pagure/docs_pagure.wsgi``
for the doc server.
If you did not install by RPM, these files are present in the sources at:
``files/pagure.wsgi`` and ``files/doc_pagure.wsgi``.
Adjust them for your needs
* Give apache permission to read the repositories owned by the ``git`` user.
For the sake of this document, we assume that the web application runs under
the ``git`` user, the same user as your gitolite user, but apache itself
runs under the ``httpd`` (or ``apache2``) user. So by default, apache
will not be allowed to read git repositories created and managed by gitolite.
To give apache this permission (required to make git clone via http work),
we use file access control lists (aka FACL):
::
setfacl -m user:apache:rx --default
setfacl -Rdm user:apache:rx /srv/git
setfacl -Rm user:apache:rx /srv/git
Where ``/srv/git`` is the home of your gitolite user (which will thus need
to be adjusted for your configuration).
* Set up the configuration file of pagure
This is an important step which concerns the file ``/etc/pagure/pagure.cfg``.
If you have installed pagure by RPM, this file is already there, otherwise
you can find an example one in the sources at: ``files/pagure.cfg.sample``
that you will have to copy to the right location.
Confer the ``Configuration`` section of this documentation for a full
explanation of all the options of pagure.
* Create the database
You first need to create the database itself. For this, since pagure can
work with: `PostgreSQL <http://www.postgresql.org/>`_,
`MySQL <http://www.mysql.com/>`_ or `MariaDB <http://mariadb.org/>`_, we
would like to invite you to consult the documentation of your database system
for this operation.
Once you have specified in the configuration file the to url used to connect
to the database, and create the database itself, you can now create the
tables, the database scheme.
To create the database tables, you need to run the script
``/usr/share/pagure/pagure_createdb.py`` and specify it the configuration
file to use via an environment variable.
For example:
::
PAGURE_CONFIG=/etc/pagure/pagure.cfg python /usr/share/pagure/pagure_createdb.py
This will tell ``/usr/share/pagure/pagure_createdb.py`` to use the database
information specified in the file ``/etc/pagure/pagure.cfg``.
.. warning:: Pagure's default configuration is using sqlite. This is fine
for development purpose but not for production use as sqlite does
not support all the operations needed when updating the database
schema. Do use PostgreSQL, MySQL or MariaDB in production.
* Stamp the alembic revision
For changes to existing tables, we rely on `Alembic <http://alembic.readthedocs.org/>`_.
It uses `revisions` to perform the upgrades, but to know which upgrades are
needed and which are already done, the current revision needs to be saved
in the database. This will allow alembic to know apply the new revision when
running it.
You can save the current revision in the database using the following command:
::
cd /etc/pagure
alembic stamp $(alembic heads |awk '{ print $1 }')
The ``cd /etc/pagure`` is needed as the command must be run in the folder
where the file ``alembic.ini`` is. This file contains two important pieces
of information:
* ``sqlalchemy.url`` which is the URL used to connect to the database, likely
the same URL as the one in ``pagure.cfg``.
* ``script_location`` which is the path to the ``versions`` folder containing
all the alembic migration files.
The ``alembic stamp`` command is the one actually saving the current revision
into the database. This current revision is found using ``alembic heads``
which returns the most recent revision found by alembic, and since the
database was just created, it is at the latest revision.
Set up virus scannining
-----------------------
Pagure can automatically scan uploaded attachments for viruses using Clam.
To set this up, first install clamav-data-empty, clamav-server,
clamav-server-systemd and clamav-update.
Then edit /etc/freshclam.conf, removing the Example line and run freshclam once
to get an up to date database.
Copy /usr/share/doc/clamav-server/clamd.conf to /etc/clamd.conf and edit that
too, again making sure to remove the Example line. Make sure to set LocalSocket
to a file in a directory that exists, and set User to an existing system user.
Then start the clamd service and set VIRUS_SCAN_ATTACHMENTS = True in the
Pagure configuration.

48
doc/install_evs.rst Normal file
View file

@ -0,0 +1,48 @@
Installing pagure's EventSource server
======================================
Eventsource or Server Sent Events are messages sent from a server to a web
browser. It allows one to refresh a page "live", ie, without the need to reload
it entirely.
Configure your system
---------------------
The eventsource server is easy to set-up.
* Install the required dependencies
::
python-redis
python-trollius
python-trollius-redis
.. note:: We ship a systemd unit file for pagure_milter but we welcome patches
for scripts for other init systems.
* Install the files of the SSE server as follow:
+----------------------------------------+-----------------------------------------------------+
| Source | Destination |
+========================================+=====================================================+
| ``ev-server/pagure-stream-server.py`` | ``/usr/libexec/pagure-ev/pagure-stream-server.py`` |
+----------------------------------------+-----------------------------------------------------+
| ``ev-server/pagure_ev.service`` | ``/etc/systemd/system/pagure_ev.service`` |
+----------------------------------------+-----------------------------------------------------+
The first file is the script of the SSE server itself.
The second file is the systemd service file.
* Finally, activate the service and ensure it's started upon boot:
::
systemctl enable redis
systemctl start redis
systemctl enable pagure_ev
systemctl start pagure_ev

79
doc/install_milter.rst Normal file
View file

@ -0,0 +1,79 @@
Installing pagure's milter
==========================
A milter is a script that is ran by a Mail Transfer Agent (`MTA
<https://en.wikipedia.org/wiki/Message_transfer_agent>`_)
upon receiving an email via either a network or an unix socket.
If you want more information feel free to check out the corresponding page
on wikipedia: `https://en.wikipedia.org/wiki/Milter
<https://en.wikipedia.org/wiki/Milter>`_.
Configure your system
---------------------
* Install the required dependencies
::
python-pymilter
.. note:: We ship a systemd unit file for pagure_milter but we welcome patches
for scripts for other init systems.
.. note:: It also requires a MTA, we used postfix.
* Create an alias ``reply``
This can be done in ``/etc/aliases``, for example:
::
reply: /dev/null
* Activate the ability of your MTA, to split users based on the character ``+``.
This way all the emails sent to ``reply+...@example.com`` will be forwarded
to your alias for ``reply``.
In postfix this is done via:
::
recipient_delimiter = +
* Hook the milter in the MTA
In postfix this is done via:
::
non_smtpd_milters = unix:/var/run/pagure/paguresock
smtpd_milters = unix:/var/run/pagure/paguresock
* Install the files of the milter as follow:
+--------------------------------------+---------------------------------------------------+
| Source | Destination |
+======================================+===================================================+
| ``milters/comment_email_milter.py`` | ``/usr/share//pagure/comment_email_milter.py`` |
+--------------------------------------+---------------------------------------------------+
| ``milters/milter_tempfile.conf`` | ``/usr/lib/tmpfiles.d/pagure-milter.conf`` |
+--------------------------------------+---------------------------------------------------+
| ``milters/pagure_milter.service`` | ``/etc/systemd/system/pagure_milter.service`` |
+--------------------------------------+---------------------------------------------------+
The first file is the script of the milter itself.
The second file is a file specific for systemd and ensuring the temporary
folders needed by the milter are re-created if needed at each boot.
The third file is the systemd service file.
* Activate the service and ensure it's started upon boot:
::
systemctl enable pagure_milter
systemctl start pagure_milter

61
doc/install_pagure_ci.rst Normal file
View file

@ -0,0 +1,61 @@
Installing pagure-ci
====================
A CI stands for `Continuous Integration
<https://en.wikipedia.org/wiki/Continuous_integration>`_. Pagure can be
configured to integrate results coming from CI services, such as `Jenkins
<https://en.wikipedia.org/wiki/Jenkins_(software)>`_ on pull-request opened
against the project.
.. note: Currently, pagure only supports `Jenkins` but we welcome help to
integrate pagure with other services such as `travis-ci
<https://en.wikipedia.org/wiki/Travis_CI>`_.
Configure your system
---------------------
* Install the required dependencies
::
python-jenkins
python-redis
python-trollius-redis
python-trollius
.. note:: We ship a systemd unit file for pagure_ci but we welcome patches
for scripts for other init systems.
* Install the files of pagure-ci as follow:
+--------------------------------------+---------------------------------------------------+
| Source | Destination |
+======================================+===================================================+
| ``pagure-ci/pagure_ci_server.py`` | ``/usr/libexec/pagure-ci/pagure_ci_server.py`` |
+--------------------------------------+---------------------------------------------------+
| ``pagure-ci/pagure_ci.service`` | ``/etc/systemd/system/pagure_ci.service`` |
+--------------------------------------+---------------------------------------------------+
The first file is the pagure-ci service itself, triggering the build on the
CI service when there is a new pull-request or a change to an existing one.
The second file is the systemd service file.
* Configure your pagure instance to support CI, add the following to your
configuration file
::
PAGURE_CI_SERVICES = ['jenkins']
* Activate the service and ensure it's started upon boot:
::
systemctl enable redis
systemctl start redis
systemctl enable pagure_ci
systemctl start pagure_ci

49
doc/install_webhooks.rst Normal file
View file

@ -0,0 +1,49 @@
Installing pagure's web-hooks notification system
=================================================
Web-hooks are a notification system upon which a system makes a http POST
request with some data upon doing an action. This allows notifying a system
that an action has occurred.
If you want more information feel free to check out the corresponding page
on wikipedia: `https://en.wikipedia.org/wiki/Webhook
<https://en.wikipedia.org/wiki/Webhook>`_.
Configure your system
---------------------
* Install the required dependencies
::
python-redis
python-trollius
python-trollius-redis
.. note:: We ship a systemd unit file for pagure_webhook but we welcome patches
for scripts for other init systems.
* Install the files of the web-hook server as follow:
+----------------------------------------------+----------------------------------------------------------+
| Source | Destination |
+==============================================+==========================================================+
| ``webhook-server/pagure-webhook-server.py`` | ``/usr/libexec/pagure-webhook/pagure-webhook-server.py`` |
+----------------------------------------------+----------------------------------------------------------+
| ``webhook-server/pagure_webhook.service`` | ``/etc/systemd/system/pagure_webhook.service`` |
+----------------------------------------------+----------------------------------------------------------+
The first file is the script of the web-hook server itself.
The second file is the systemd service file.
* Activate the service and ensure it's started upon boot:
::
systemctl enable redis
systemctl start redis
systemctl enable pagure_webhook
systemctl start pagure_webhook

62
doc/milter.rst Normal file
View file

@ -0,0 +1,62 @@
Pagure's Milter
===============
`Milter <http://www.postfix.org/MILTER_README.html>`_ are script executed by
postfix upon sending or receiving an email.
We use this system to allow pagure's users to comment on a ticket (or a
pull-request) by directly replying to the email sent as a notification.
Pagure's milter is designed to be run on the same machine as the mail server
(postfix by default). Postfix connecting to the milter via a unix socket.
The milter itself is a service managed by systemd.
You can find all the relevant files for the milter under the ``milters`` folder
in the sources.
Install the milter
------------------
The first step to enable the milter on a pagure instance is thus to install the
``.service`` file for systemd and place the corresponding script that, by
default, should go to ``/usr/share/pagure/comment_email_milter.py``.
If you are using the RPM, install ``pagure-milters`` should provide and install
all the files correctly.
Activate the milter
-------------------
Make sure the milter is running and will be automaticall started at boot by
running the commands:
To start the milter:
::
systemctl start pagure_milter
To ensure the milter is always started at boot time:
::
systemctl enable pagure_milter
Activate the milter in postfix
------------------------------
To actually activate the milter in postfix is in fact really easy, all it takes
is two lines in the ``main.cf`` file of postfix:
::
non_smtpd_milters = unix:/var/run/pagure/paguresock
smtpd_milters = unix:/var/run/pagure/paguresock
These two lines are pointing to the unix socket used by postfix to communicate
with the milter. This socket is defined in the milter file itself, in the
sources: ``milters/comment_email_milter.py``.

53
doc/overview.ascii Normal file
View file

@ -0,0 +1,53 @@
Grants/Denies access
+------------+ +-----------+
| | | |
User's git actions+---------------------->| Gitolite +-------------------------->+ Git repos |
| | | |
+-----+------+ +-----------+
^
|
|
+------------------------------------------+
|
|
+-------------------------+ |
Notifications | | |
+------------------------------------+ Postfix |<--------------------------------+
| | | | |
| | +-------------------+ | |
| | | | | |
v | | Pagure's milter | | |
User's mail client | | +--------------+ | |
+ +-----+--------+----------+ | | |
| ^ Updates | | |
| | | | |
| Replies | | | |
+---------------------------------------------------+ | | |
| | |
| | |
+--------------+ | | |
| | | | |
+----------------------->| Pagure | v | |
| | Doc server | +------------+-+ |
| | | |{s} | |
| +--------------+ +------->| Database | |
| | | | |
User's web browser--+ http requests Updates | +--------------+ |
^ | & queries| |
| | | |
| | +--------------+ | |
| | | +----------+---------------------------------+
| +----------------------->| Pagure |
| | web server +---+ +----------------------+ +----------------+
| | | | | | | |
| +--------------+ | | Pagure | | Third Party |
| +---------->| Web hooks' server +-------------->| Services |
| | | | | |
| redis | +----------------------+ +----------------+
| |
| | +----------------------+
| +---------->| |
| | Pagure |
+----------------------------------------------------------------------+ EventSource server |
Server-Sent Event | |
+----------------------+

104
doc/overview.rst Normal file
View file

@ -0,0 +1,104 @@
Overview
========
Pagure is split over multiple components, each having their purpose and all
but one (the core application) being optional.
These components are:
.. contents::
Before going into the overall picture, one should realize that most of the
components listed above are optional.
Here is a diagram representing pagure without all the optionals components:
.. image:: _static/overview_simple.png
:target: _static/overview_simple.png
And here is a diagram of all the components together:
.. image:: _static/overview.png
:target: _static/overview.png
Pagure core application
-----------------------
The core application is the flask application interacting with gitolite to
provide a web UI to the git repositories as well as tickets and pull-requests.
This is the main application for the forge.
Gitolite
--------
Currently pagure uses `gitolite <http://gitolite.com/gitolite/index.html>`_
to grant or deny `ssh <https://en.wikipedia.org/wiki/Secure_Shell>`_ access
to the git repositories, in other words to grant or deny read and/or write
access to the git repositories.
Pagure supports cloning over both ssh and http, but writing can only be done
via ssh, through gitolite.
Pagure doc server
-----------------
While integrated into the main application at first, it has been split out
for security concern, displaying information directly provided by the user
without a clear/safe way of filtering for un-safe script or hacks is a
security hole.
For this reason we also strongly encourage anyone wanting to deploy their
own instance of pagure with the doc server, to run this application on a
completely different domain name (not just a sub-domain) in order to reduce
the cross-site forgery risks.
Pagure can be run just fine without the doc server, all you need to do is to
**not** define the variable ``DOC_APP_URL`` in the configuration file.
Pagure milter
-------------
The milter is a script, receiving an email as input and performing an action
with it.
In the case of pagure, the milter is used to allow replying on a comment
of a ticket or a pull-request by directly replying to the notification sent.
No need to go to the page anymore to reply to a comment someone made.
The milter integrates with a MTA such as postfix or sendmail that you will
have running and have access to in order to change its configuration.
Pagure EventSource Server
-------------------------
Eventsource or Server Sent Events are messages sent from a server to a browser.
For pagure this technology is used to allow live-refreshing of a page when
someone is viewing it. For example, while you are reading a ticket if someone
comments on it, the comment will automatically show up on the page without
the need for you to reload the entire page.
The flow is: the main pagure server does an action, sends a message over
redis, the eventsource server picks it up and send it to the browsers waiting
for it, then javascript code is executed to refresh the page based on the
information received.
Pagure web-hook Server
-------------------------
Sends notifications to third party services using POST http requests.
This is the second notifications system in pagure with `fedmsg <http://fedmsg.com/>`_.
These notifications are running on their own service to prevent blocking the
main web application in case the third part service is timing-out or just
being slow.
The flow is: the main pagure server does an action, sends a message over
redis, the web-hook server picks it up, build the query and performs the
POST request to the specified URLs.

26
doc/overview_simple.ascii Normal file
View file

@ -0,0 +1,26 @@
Grants/Denies access
+------------+ +-----------+
|cfffca4 | | |
User's git actions+--------------------->+ Gitolite +-------------------------->+ Git repos |
| | | |
+------------+ +-----------+
^
|
+-----------------------------+
|
User's mail client |
^ +---------------+ |
| Notifications | | |
+----------------------------------+ Mail server | |
| | |
+---------------+ |
^ |
| |
| |
+--------------+ Updates +--------------+
| | & queries |{s} |
User's web browser+---------------------->+ Pagure +------------->| Database |
| web server | | |
| | +--------------+
+--------------+

2
doc/requirements.txt Normal file
View file

@ -0,0 +1,2 @@
sphinx
cloud_sptheme

44
doc/usage.rst Normal file
View file

@ -0,0 +1,44 @@
Usage
=====
Using pagure should come fairly easily, especially to people already used
to forges such as GitHub or GitLab. There are however some tips and tricks
which can be useful to know and that this section of the doc covers.
One of the major difference with GitHub and GitLab is that for each project
on pagure, four git repositories are made available to the admins of the
project:
* A git repository containing the source code, displayed in the main section
of the pagure project.
* A git repository for the documentation
* A git repository for the issues and their metadata
* A git repository for the metadata for pull-requests
You can find the URLs to access or clone these git repositories on the
overview page of the project. On the menu on the right side, there is a menu
`Source GIT URLs`, next to it is a little `more` button, by clicking on it
you will be given the URLs to the other three git repos.
Each section correspond to one of the four git repositories created for each
project.
Contents:
.. toctree::
:maxdepth: 2
usage/first_steps
usage/project_settings
usage/roadmap
usage/using_doc
usage/using_webhooks
usage/ticket_templates
usage/pr_custom_page
usage/theming
usage/upgrade_db
usage/pagure_ci

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

94
doc/usage/first_steps.rst Normal file
View file

@ -0,0 +1,94 @@
First Steps on pagure
=====================
When coming to pagure for the first time there are a few things one should
do or check to ensure all works as desired.
Login to pagure or create your account
--------------------------------------
Pagure has its own user account system.
For instances of pagure such as the one at `pagure.io <https://pagure.io>`_
where the authentication is delegated to a third party (in the case of
pagure.io, the Fedora Account System) via OpenID, the local user account
is created upon login.
This means, you cannot be added to a group or a project before you login for
the first time as the system will simply not know you.
If you run your own pagure instance which uses the local authentication
system, then you will find on the login page an option to create a new
account.
Upload your SSH key
-------------------
Pagure uses gitolite to manage who has read/write access to which git
repository via `ssh <https://en.wikipedia.org/wiki/Secure_Shell>`_.
An ssh key is composed of two parts:
* a private key, which you must keep to yourself and never share with anyone.
* a public key, which is public and therefore can be shared with anyone.
If you have never generated a ssh key, you can do so by running:
::
ssh-keygen
or alternatively on GNOME using the application ``seahorse``.
This will create two files in ``~/.ssh/`` (``~`` is the symbol for your home
folder).
These two files will be named (for example) ``id_rsa`` and ``id_rsa.pub``.
The first one is the private key that must never be shared. The second is
the public key that can be uploaded on pagure to give you ssh access.
To upload your public key onto pagure:
1. Login into pagure and click on the user icon on
the top right corner, there, select ``My settings``.
.. image:: _static/pagure_my_settings.png
:target: _static/pagure_my_settings.png
2. In the authentication section of your user settings copy the content of your
``id_rsa.pub`` file in the Public SSH key text box and save your ssh key settings.
.. image:: _static/pagure_add_ssh_key.png
:target: _static/pagure_add_ssh_key.png
.. note:: Pagure support multiple ssh keys per user, to add more than a ssh key
to your user account just add your new ssh key in your authentication
settings (one key per row), this way you will be able to push commits
to your repository from a different computer.
Configure the default email address
-----------------------------------
If the pagure instance you use is using local user authentication, then when
you created your account you could choose whichever email address you prefer
to use, but in the case (like pagure.io) where the pagure instance relies
on an external authentication service, the email address provided by this
service may be different from the one you prefer.
Your settings' page (cf the image above for how to access to the page) allow
you to add multiple email address and set one as default.
Your default email address is the address that will be used to send you
notifications and also as the email address in the git commit if you merge
a pull-request with a merge commit.
For online editing, when doing the commit, you will be presented with the
list of valid email addresses associated with your account and you will be
able to choose which one you wish to use.
.. note:: All email address will need to be confirmed to be activated, this
is done via a link sent by email to the address. If you do not
receive this link, don't forget to check your spam folder!

19
doc/usage/pagure_ci.rst Normal file
View file

@ -0,0 +1,19 @@
Pagure CI
=========
Pagure CI is a service integrating the results of Continuous Integration (CI)
services, such as jenkins or travis-ci, into pull-requests opened against
your project on pagure.
.. note:: By default pagure-ci is off, an admin of your pagure instance will
need to configure it to support one or more CI services. Check the
configuration section on how to do that.
Contents:
.. toctree::
:maxdepth: 2
pagure_ci_jenkins

View file

@ -0,0 +1,92 @@
Jenkins with Pagure-ci
======================
Jenkins is a Continuous Integration service that can be configured to be
integrated with pagure.
This document describe the steps needed to make it work.
How to enable Pagure CI
-----------------------
* Visit the settings page of your project
* Scroll down to the `Hooks` section and click on `Pagure CI`
* Select the type of CI service you want
* Enter the URL to the project on the CI service. For example, if your
project is running at `http://jenkins.fedoraproject.org` you will need to
enter the url: `http://jenkins.fedoraproject.org/job/<projectname>`
* Tick the checkbox activating the hook.
These steps will activate the hook, after reloading the page or the tab, you
will be given access to two important values: the token used to trigger the
build on jenkins and the URL used by jenkins to report the status of the
build.
Keep these two available when configuring jenkins for your project.
Configure Jenkins
-----------------
These steps can only be made by the admins of your jenkins instance, but
they only need to be made once.
* Download the following plugins:
* `Git Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin>`_
* `Notification Plugin <https://wiki.jenkins-ci.org/display/JENKINS/Notification+Plugin>`_
Configure your project on Jenkins
---------------------------------
* Go to the `Configure` page of your project
* Under `Job Notification` click `Add Endpoint`
* Fields in Endpoint will be :
::
FORMAT: JSON
PROTOCOL: HTTP
EVENT: Job Finalized
URL: <The URL provided in the Pagure CI hook on pagure>
TIMEOUT: 3000
LOG: 1
* Tick the checkbox `This build is parameterized`
* Add two `String Parameters` named REPO and BRANCH
* Source Code Management select Git and give the URL of the pagure project
* Under Build Trigger click on Trigger build remotely and specify the token
given by pagure.
* Under Build -> Add build step -> Execute Shell
* In the box given enter the shell steps you want for testing your project.
Example Script
::
# Script specific for Pull-Request build
if [ -n "$REPO" -a -n "$BRANCH" ]; then
git remote rm proposed || true
git remote add proposed "$REPO"
git fetch proposed
git checkout origin/master
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git merge --no-ff "proposed/$BRANCH" -m "Merge PR"
fi
# Part of the script specific to how you run the tests on your project

View file

@ -0,0 +1,74 @@
Customize the PR page
=====================
Pagure offers the possibility to customize the page that creates pull-request
to add your specific information, such as: please follow the XYZ coding style,
run the tests or whatever you wish to inform contributors when they open a
new pull-request.
The customization is done via a file in the git repository containing the
meta-data for the pull-requests. This file must be placed under a ``templates``
folder, be named ``contributing.md`` and can be formatted as you wish using
markdown.
Example
-------
For a project named ``test`` on ``pagure.io``.
* First, clone the pull-request git repo [#f1]_ and move into it
::
git clone ssh://git@pagure.io/requests/test.git
cd test
* Create the templates folder
::
mkdir templates
* Create the customized PR info
::
vim templates/contributing.md
And place in this file the following content:
::
Contributing to test
====================
When creating a pull-request against test, there are couple of items to do
that will speed up the review process:
* Ensure the unit-tests are all passing (cf the ``runtests.sh`` script at the
top level of the sources)
* Check if your changes are [pep8](https://www.python.org/dev/peps/pep-0008/)
compliant for this you can install ``python-pep8`` and run the ``pep8`` CLI
tool
* Commit and push the changes to the git repo
::
git add templates
git commit -m "Customize the PR page"
git push
* And this is how it will look like
.. image:: _static/pagure_custom_pr.png
:target: _static/pagure_custom_pr.png
.. [#f1] All the URLs to the different git repositories can be found on the
main page of the project, on the right-side menu, under the section
``Source GIT URLs``, click on ``more`` to see them.

View file

@ -0,0 +1,147 @@
Project settings
================
Each project have a number of options that can be tweaked in the settings
page of the project which is accessible to the person having full commits
to the project.
This page presents the different settings and there effect.
`Activate always merge`
------------------------
This boolean enables or disables always making a merge commit when merging
a pull-request.
When merging a pull-request in pagure there are three states:
* fast-forward: when the commits in the pull-request can be fast-forwarded
pagure signals it and just fast-forward the commit, keeping the history linear.
* merge: when the commits in the pull-request cannot be merged without a merge
commit, pagure signals it and performs this merge commit.
* conflicts: when the commits in the pull-request cannot be merged at all
automatically due to one or more conflicts. Then pagure signals it and prevent
merging.
If the `Activate always merge` option is on, then the `fast-forward` option
above is disabled in favor of the `merge` option.
`Activate comment editing`
--------------------------
This boolean enables or disables editing comments.
After commenting on a ticket or a pull-request, the admins of the project
and the author of the comment may be allowed to edit the comment.
This allows them to adjust the wording or the style as they wish.
.. note:: notification about a comment is only sent once with the original
text, changes performed later will not trigger a new notification.
Some project may not want to allow editing comments after they were posted
and this setting allows turning it on or off.
`Activate Enforce signed-off commits in pull-request`
-----------------------------------------------------
This boolean enables or disables checking for a 'Signed-off-by' line (case
insensitive) in the commit messages of the pull-requests.
If this line is missing, pagure will display a message near the `Merge`
button, allowing project admin to request the PR to be updated.
.. note:: This setting does not prevent commits without this 'signed-off-by'
line to be pushed directly, it only work at the pull-request level.
`Activate issue tracker`
------------------------
This boolean simply enables or disables the issue tracker for the project.
So if you are tracking your ticket on a different system, you can simply
disable reporting issue on pagure by un-checking this option.
`Activate Minimum score to merge pull-request`
----------------------------------------------
This option can be used for project wishing to enforce having a minimum
number of people reviewing a pull-request before it can be merged.
If this option is enabled, anyone can vote in favor or against a pull-request
and the sum of the votes in favor minus the sum of the votes againsts give
the pull-request a score that should be equal or great to the value
entered in this option for the pull-request to be allowed to be merged.
.. note:: Only the main comments (ie: not in-line) are taken into account
to calculate the score of the pull-request.
To vote in favor of a pull-request, use either:
* ``+1``
* ``:thumbsup:``
To vote against a pull-request, use either:
* ``-1``
* ``:thumbsdown:``
.. note:: Pull-Request reaching the minimum score are not automatically merged
.. note:: Anyone can vote on the pull-request, not only the contributors.
`Activate Only assignee can merge pull-request`
-----------------------------------------------
This option can be used for project wishing to institute a strong review
workflow where pull-request are first assigned then merged.
If this option is enabled, only the person assigned to the pull-request
can merge it.
`Activate project documentation`
--------------------------------
Pagure offers the option to have a git repository specific for the
documentation of the project.
This repository is then accessible under the ``Docs`` tab in the menu of the
project.
If you prefer to store your documentation elsewhere or maybe even within
the sources of the project, you can disable the ``Docs`` tab by un-checking
this option.
`Activate pull requests`
------------------------
Pagure offers the option to fork a project, make changes to it and then ask
the developer to merge these changes into the project. This is similar to
the pull-request mechanism on GitHub or GitLab.
However, some projects may prefer receiving patches by email on their list
or via another hosting plateform or simply do not wish to use the
pull-request mechanism at all. Un-checking this option will therefore
prevent anyone from opening a pull-request against this project.
.. note:: disabling pull-requests does *not* disable forking the projects.
`Activate Web-hooks`
--------------------
Pagure offers the option of sending notification about event happening on a
project via [web-hooks|https://en.wikipedia.org/wiki/Webhook]. This option
is off by default and can be turned on for a pagure instance in its
configuration file.
The URL of the web-hooks can be entered in this field.
.. note:: See the ``notifications`` documentation to learn more about
web-hooks in pagure and how to use them.

36
doc/usage/roadmap.rst Normal file
View file

@ -0,0 +1,36 @@
Using the roadmap feature
=========================
Pagure allows building the roadmap of the project using the tickets and
their milestones.
The principal is as follow:
* For each milestones defined in the settings of the project, the roadmap
will group tickets with the corresponding milestone.
* If your project has an ``unplanned`` milestone, this milestone will be
shown at the bottom of the roadmap page. This allowing you to put something
on the roadmap without assigning a real milestone to it.
Example
-------
For a project named ``test`` on ``pagure.io``.
* First, go to the settings page of the project, create the milestones you
like, for example: ``v1.0`` and ``v2.0``.
* For the tickets you want to be on these milestones, go through each of them
and set their milestone to either ``v1.0`` or ``v2.0``, or none of them
if the ticket is not on the roadmap.
You can set the milestone on the metadata panel on the right side of the
issue page.
* And this is how it will look like
.. image:: _static/pagure_roadmap2.png
:target: _static/pagure_roadmap2.png

81
doc/usage/theming.rst Normal file
View file

@ -0,0 +1,81 @@
Theme your pagure
=================
Pagure via `flask-multistatic <https://pagure.io/flask-multistatic>`_
offers the possibility to override the default theme allowing to customize
the style of your instance.
By default pagure looks for its templates and static files in the folders
``pagure/templates`` and ``pagure/static``, but you can ask pagure to look
for templates and static files in another folder.
By specifying the configuration keys ``THEME_TEMPLATE_FOLDER`` and
``THEME_STATIC_FOLDER`` in pagure's configuration file, you tell pagure to
look for templates and static files first in these folders, then in its
usual folders.
.. note: The principal is that pagure will look in the folder specified in
the configuration file first and then in its usual folder, so the
**file names must be identical**.
Example
-------
Let's take an example, you wish to replace the pagure logo at the top right
of all the pages.
This logo is part of the ``master.html`` template which all pages inherit
from. So what you want to do is replace this ``master.html`` by your own.
* First, create the folder where your templates and static files will be stored:
::
mkdir /var/www/mypaguretheme/templates
mkdir /var/www/mypaguretheme/static
* Place your own logo in the static folder
::
cp /path/to/your/my-logo.png /var/www/mypaguretheme/static
* Place in there the original ``master.html``
::
cp /path/to/original/pagure/templates/master.html /var/www/mypaguretheme/templates
* Edit it and replace the url pointing to the pagure logo (around line 27)
::
- <img height=40px src="{{ url_for('static', filename='pagure-logo.png') }}"
+ <img height=40px src="{{ url_for('static', filename='my-logo.png') }}"
* Adjust pagure's configuration file:
+ THEME_TEMPLATE_FOLDER='/var/www/mypaguretheme/templates'
+ THEME_STATIC_FOLDER='/var/www/mypaguretheme/static'
* Restart pagure
.. note: you could just have replaced the `pagure-logo.png` file with your
own logo which would have avoided overriding the template.
In production
-------------
Serving static files via flask is fine for development but in production
you will probably want to have apache server them. This will allow caching
either on the server side or on the client side.
You can ask apache to behave in a similar way as does flask-multistatic with
flask here, ie: search in one folder and if you don't find the file look
in another one.
`An example apache configuration <https://pagure.io/flask-multistatic/blob/master/f/example.conf>`_
is provided as part of the sources of `flask-multistatic`_.

View file

@ -0,0 +1,77 @@
Templates for ticket input
==========================
Pagure offers the possibility to add templates for ticket's input. These
templates do not enforce anything, users will have the possibility to simply
ignore it, or even to not follow it, but it also helps structuring the
ticket opened against a project and highlighting the information that are
often requested/needed.
The templates are provided in the git repository containing the meta-data
for the tickets.
They must be placed under a ``templates`` folder in this git repository,
end with ``.md``and as the extension suggests can be formatted as markdown.
If you create a template ``templates/default.md``, it will be shown by
default when someone ask to create a new ticket.
Example
-------
For a project named ``test`` on ``pagure.io``.
* First, clone the ticket git repo [#f1]_ and move into it
::
git clone ssh://git@pagure.io/tickets/pagure.git
cd test
* Create the templates folder
::
mkdir templates
* Create a default template
::
vim templates/default.md
And place in this file the following content:
::
##### Issue
##### Steps to reproduce
1.
2.
3.
##### Actual results
##### Expected results
* Commit and push the changes to the git repo
::
git add templates
git commit -m "Add a default template for tickets"
git push
* And this is how it will look like
.. image:: _static/pagure_ticket_template.png
:target: _static/pagure_ticket_template.png
.. [#f1] All the URLs to the different git repositories can be found on the
main page of the project, on the right-side menu, under the section
``Source GIT URLs``, click on ``more`` to see them.

48
doc/usage/upgrade_db.rst Normal file
View file

@ -0,0 +1,48 @@
Upgrade a database
==================
Database schema migration are handled in two ways:
* New tables
For this we simply rely on the ``createdb`` script used when creating the
database the first time.
* Changes to existing tables
For changes to existing tables, we rely on `Alembic <http://alembic.readthedocs.org/>`_.
This allows us to do upgrade and downgrade of schema migration, kind of like
one would do commits in a system like git.
To upgrade the database to the latest version simply run:
::
alembic upgrade head
This may fail for different reasons:
* The change was already made in the database
This can be because the version of the database schema saved is incorrect.
It can be debugged using the following commands:
* Find the current revision: ``alembic current``
* See the entire history: ``alembic history``
Once the revision at which your database should be is found (in the history)
you can declare that your database is at this given revision using:
``alembic stamp <revision id>``.
Eventually, if you do not know where your database is or should be, you can
do an iterative process stamping the database for every revision, one by one
trying everytime to ``alembic upgrade`` until it works.
* The database used does not support some of the changes
SQLite is handy for development but does not support all the features of a
real database server. Upgrading a SQLite database might therefore not work,
depending on the changes done.
In some cases, if you are using a SQLite database, you will have to destroy
it and create a new one.

108
doc/usage/using_doc.rst Normal file
View file

@ -0,0 +1,108 @@
Using the doc repository of your project
========================================
In this section of the documentation, we are interested in the doc repository.
The doc repository is a simple git repo, whose content will appear under the
`Docs` tab in pagure and on https://docs.pagure.org/<project>/.
There are a few ways you can put your documentation in this repo:
* Simple text files
Pagure will display them as plain text. If one of these is named ``index``
it will be presented as the front page.
* rst or markdown files
Pagure will convert them to html on the fly and display them as such.
The rst files must end with `.rst` and the markdown ones must end with
``.mk``, ``.md`` or simply ``.markdown``.
* html files
Pagure will simply show them as such.
.. note: By default the `Docs` tab in the project's menu is disabled, you
will have to visit the project's settings page and turn it on
in the ``Project options`` section.
Example
-------
Pagure's documentation is kept in pagure's sources, in the `doc` folder there.
You can see it at: `https://pagure.io/pagure/blob/master/f/doc
<https://pagure.io/pagure/blob/master/f/doc>`_. This doc can be built with
`sphinx <http://sphinx-doc.org/>`_ to make it html and prettier.
The built documentation is available at: `https://docs.pagure.org/pagure/
<https://docs.pagure.org/pagure/>`_.
This is how it is built/updated:
* Clone pagure's sources::
git clone https://pagure.io/pagure.git
* Move into its doc folder::
cd pagure/doc
* Build the doc::
make html
* Clone pagure's doc repository::
git clone ssh://git@pagure.io/docs/pagure.git
* Copy the result of sphinx's build to the doc repo::
cp -r _build/html/* pagure/
* Go into the doc repo and update it::
cd pagure
git add .
git commit -am "Update documentation"
git push
* Clean the sources::
cd ..
rm -rf pagure # remove the doc repo
rm -rf _build # remove the output from the sphinx's build
To make things simpler, the following script (name `update_doc.sh`) can be
used:
::
#!/bin/bash
make html
git clone "ssh://git@pagure.io/docs/$1.git"
cp -r _build/html/* $1/
(
cd $1
git add .
git commit -av
git push
)
rm -rfI _build
rm -rfI $1
It can be used by running `update_doc.sh <project>` from within the folder
containing the doc.
So for pagure it would be something like:
::
cd pagure/doc
update_doc.sh pagure

View file

@ -0,0 +1,57 @@
Using web-hooks
===============
Web-hooks are a notification system that could be compared to a callback.
Basically, pagure will make a HTTP POST request to one or more third party
server/application with information about what is or just happened.
To set-up a web-hook, simply go to the settings page of your project and
enter the URL to the server/endpoint that will receive the notifications.
There is, in the settings page, a web-hook key which is used by the
server (here pagure) to sign the message sent and which you can use to
ensure the notifications received are coming from the right source.
Each POST request made contains two specific headers:
::
X-Pagure-Topic
X-Pagure-Signature
``X-Pagure-Topic`` is a global header giving a clue about the type of action
that just occurred. For example ``issue.edit``.
``X-Pagure-Signature`` contains the signature of the message allowing to
check that the message comes from pagure.
.. warning:: These headers are present for convenience only, they are not
signed and therefore should not be trusted. Rely on the payload
after checking the signature to make any decision.
Pagure relies on ``hmac`` to sign the content of its messages. If you want
to validate the message, in python, you can do something like the following:
::
import hmac
import hashlib
payload = # content you received in the POST request
headers = # headers of the POST request
project_web_hook_key = # private web-hook key of the project
hashhex = hmac.new(
str(project_web_hook_key), payload, hashlib.sha1).hexdigest()
if hashhex != headers.get('X-Pagure-Signature'):
raise Exception('Message received with an invalid signature')
The notifications sent via web-hooks have the same payload as what is sent
via `fedmsg <http://www.fedmsg.com/en/latest/>`_. Therefore, the list of
pagure topics as well as example messages can be found in the
`fedmsg documentation about pagure
<https://fedora-fedmsg.readthedocs.org/en/latest/topics.html#id532>`_

View file

@ -0,0 +1,248 @@
#!/usr/bin/env python
"""
(c) 2015 - Copyright Red Hat Inc
Authors:
Pierre-Yves Chibon <pingou@pingoured.fr>
Streaming server for pagure's eventsource feature
This server takes messages sent to redis and publish them at the specified
endpoint
To test, run this script and in another terminal
nc localhost 8080
HELLO
GET /test/issue/26?foo=bar HTTP/1.1
"""
import datetime
import logging
import os
import urlparse
import trollius
import trollius_redis
log = logging.getLogger(__name__)
if 'PAGURE_CONFIG' not in os.environ \
and os.path.exists('/etc/pagure/pagure.cfg'):
print 'Using configuration file `/etc/pagure/pagure.cfg`'
os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
import pagure
import pagure.lib
from pagure.exceptions import PagureEvException
SERVER = None
def get_obj_from_path(path):
""" Return the Ticket or Request object based on the path provided.
"""
username = None
try:
if path.startswith('/fork'):
username, repo, obj, objid = path.split('/')[2:6]
else:
repo, obj, objid = path.split('/')[1:4]
except:
raise PagureEvException("Invalid URL: %s" % path)
repo = pagure.lib.get_project(pagure.SESSION, repo, user=username)
if repo is None:
raise PagureEvException("Project '%s' not found" % repo)
output = None
if obj == 'issue':
if not repo.settings.get('issue_tracker', True):
raise PagureEvException("No issue tracker found for this project")
output = pagure.lib.search_issues(
pagure.SESSION, repo, issueid=objid)
if output is None or output.project != repo:
raise PagureEvException("Issue '%s' not found" % objid)
if output.private:
# TODO: find a way to do auth
raise PagureEvException(
"This issue is private and you are not allowed to view it")
elif obj == 'pull-request':
if not repo.settings.get('pull_requests', True):
raise PagureEvException(
"No pull-request tracker found for this project")
output = pagure.lib.search_pull_requests(
pagure.SESSION, project_id=repo.id, requestid=objid)
if output is None or output.project != repo:
raise PagureEvException("Pull-Request '%s' not found" % objid)
else:
raise PagureEvException("Invalid object provided: '%s'" % obj)
return output
@trollius.coroutine
def handle_client(client_reader, client_writer):
data = None
while True:
# give client a chance to respond, timeout after 10 seconds
line = yield trollius.From(trollius.wait_for(
client_reader.readline(),
timeout=10.0))
if not line.decode().strip():
break
line = line.decode().rstrip()
if data is None:
data = line
if data is None:
log.warning("Expected ticket uid, received None")
return
data = data.decode().rstrip().split()
log.info("Received %s", data)
if not data:
log.warning("No URL provided: %s" % data)
return
if not '/' in data[1]:
log.warning("Invalid URL provided: %s" % data[1])
return
url = urlparse.urlsplit(data[1])
try:
obj = get_obj_from_path(url.path)
except PagureEvException as err:
log.warning(err.message)
return
origin = pagure.APP.config.get('APP_URL')
if origin.endswith('/'):
origin = origin[:-1]
client_writer.write((
"HTTP/1.0 200 OK\n"
"Content-Type: text/event-stream\n"
"Cache: nocache\n"
"Connection: keep-alive\n"
"Access-Control-Allow-Origin: %s\n\n" % origin
).encode())
connection = yield trollius.From(trollius_redis.Connection.create(
host=pagure.APP.config['REDIS_HOST'],
port=pagure.APP.config['REDIS_PORT'],
db=pagure.APP.config['REDIS_DB']))
try:
# Create subscriber.
subscriber = yield trollius.From(connection.start_subscribe())
# Subscribe to channel.
yield trollius.From(subscriber.subscribe(['pagure.%s' % obj.uid]))
# Inside a while loop, wait for incoming events.
while True:
reply = yield trollius.From(subscriber.next_published())
#print(u'Received: ', repr(reply.value), u'on channel', reply.channel)
log.info(reply)
log.info("Sending %s", reply.value)
client_writer.write(('data: %s\n\n' % reply.value).encode())
yield trollius.From(client_writer.drain())
except trollius.ConnectionResetError as err:
log.exception("ERROR: ConnectionResetError in handle_client")
except Exception as err:
log.exception("ERROR: Exception in handle_client")
finally:
# Wathever happens, close the connection.
connection.close()
client_writer.close()
@trollius.coroutine
def stats(client_reader, client_writer):
try:
log.info('Clients: %s', SERVER.active_count)
client_writer.write((
"HTTP/1.0 200 OK\n"
"Cache: nocache\n\n"
).encode())
client_writer.write(('data: %s\n\n' % SERVER.active_count).encode())
yield trollius.From(client_writer.drain())
except trollius.ConnectionResetError as err:
log.info(err)
pass
finally:
client_writer.close()
return
def main():
global SERVER
try:
loop = trollius.get_event_loop()
coro = trollius.start_server(
handle_client,
host=None,
port=pagure.APP.config['EVENTSOURCE_PORT'],
loop=loop)
SERVER = loop.run_until_complete(coro)
log.info('Serving server at {}'.format(SERVER.sockets[0].getsockname()))
if pagure.APP.config.get('EV_STATS_PORT'):
stats_coro = trollius.start_server(
stats,
host=None,
port=pagure.APP.config.get('EV_STATS_PORT'),
loop=loop)
stats_server = loop.run_until_complete(stats_coro)
log.info('Serving stats at {}'.format(
stats_server.sockets[0].getsockname()))
loop.run_forever()
except KeyboardInterrupt:
pass
except trollius.ConnectionResetError as err:
log.exception("ERROR: ConnectionResetError in main")
except Exception as err:
log.exception("ERROR: Exception in main")
finally:
# Close the server
SERVER.close()
if pagure.APP.config.get('EV_STATS_PORT'):
stats_server.close()
log.info("End Connection")
loop.run_until_complete(SERVER.wait_closed())
loop.close()
log.info("End")
if __name__ == '__main__':
log = logging.getLogger("")
formatter = logging.Formatter(
"%(asctime)s %(levelname)s [%(module)s:%(lineno)d] %(message)s")
# setup console logging
log.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
aslog = logging.getLogger("asyncio")
aslog.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
log.addHandler(ch)
main()

View file

@ -0,0 +1,14 @@
[Unit]
Description=Pagure EventSource server (Allowing live refresh of the pages supporting it)
After=redis.target
Documentation=https://pagure.io/pagure
[Service]
ExecStart=/usr/libexec/pagure-ev/pagure-stream-server.py
Type=simple
User=git
Group=git
Restart=on-failure
[Install]
WantedBy=multi-user.target

60
files/alembic.ini Normal file
View file

@ -0,0 +1,60 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = /usr/share/pagure/alembic
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
#sqlalchemy.url = driver://user:pass@localhost/dbname
sqlalchemy.url = sqlite:////var/tmp/pagure_dev.sqlite
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View file

@ -0,0 +1,55 @@
#!/usr/bin/env python
import os
import argparse
from datetime import datetime, timedelta
from sqlalchemy.exc import SQLAlchemyError
if 'PAGURE_CONFIG' not in os.environ \
and os.path.exists('/etc/pagure/pagure.cfg'):
print 'Using configuration file `/etc/pagure/pagure.cfg`'
os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
import pagure
from pagure import SESSION
from pagure.lib import model
def main(debug=False):
''' The function that actually sends the email
in case the expiration date is near'''
current_time = datetime.utcnow()
day_diff_for_mail = [5, 3, 1]
email_dates = [email_day.date() for email_day in \
[current_time + timedelta(days=i) for i in day_diff_for_mail]]
tokens = SESSION.query(model.Token).all()
for token in tokens:
if token.expiration.date() in email_dates:
user = token.user
user_email = user.default_email
project = token.project
days_left = token.expiration.day - datetime.utcnow().day
subject = 'Pagure API key expiration date is near!'
text = '''Hi %s, \nYour Pagure API key for the project %s will expire
in %s day(s). Please get a new key for non-interrupted service. \n
Thanks, \nYour Pagure Admin. ''' % (user.fullname, project.name, days_left)
msg = pagure.lib.notify.send_email(text, subject, user_email)
if debug:
print 'Sent mail to %s' % user.fullname
if debug:
print 'Done'
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Script to send email before the api token expires')
parser.add_argument(
'--debug', dest='debug', action='store_true', default=False,
help='Print the debugging output')
args = parser.parse_args()
main(debug=args.debug)

23
files/doc_pagure.wsgi Normal file
View file

@ -0,0 +1,23 @@
#-*- coding: utf-8 -*-
# The three lines below are required to run on EL6 as EL6 has
# two possible version of python-sqlalchemy and python-jinja2
# These lines make sure the application uses the correct version.
import __main__
__main__.__requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4']
import pkg_resources
# Set the environment variable pointing to the configuration file
import os
os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
# The following is only needed if you did not install pagure
# as a python module (for example if you run it from a git clone).
#import sys
#sys.path.insert(0, '/path/to/pagure/')
# The most import line to make the wsgi working
from pagure.docs_server import APP as application
#application.debug = True

32
files/emoji_clean_json.py Normal file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env python3
import json
import os
import sys
data = None
with open('emoji_strategy.json') as stream:
data = json.load(stream)
if not data:
print 'Could not load the data from the JSON file'
sys.exit(1)
# Retrieve the items we keep in the JSON
tokeep = {}
for key in data:
if '-' in data[key]['unicode'] and data[key]['unicode'].startswith('1F'):
continue
tokeep[key] = data[key]
# Check if we have the keys of all images we kept
unicodes = [tokeep[key]['unicode'] for key in tokeep]
images = [item.replace('.png', '') for item in os.listdir('png')]
print set(unicodes).symmetric_difference(set(images))
with open('emoji_strategy2.json', 'w') as stream:
json.dump(tokeep, stream)

233
files/gitolite.rc Executable file
View file

@ -0,0 +1,233 @@
# paths and configuration variables for gitolite
# please read comments before editing
# this file is meant to be pulled into a perl program using "do" or "require".
# You do NOT need to know perl to edit the paths; it should be fairly
# self-explanatory and easy to maintain perl syntax :-)
# --------------------------------------
# Do not uncomment these values unless you know what you're doing
# $GL_PACKAGE_CONF = "";
# $GL_PACKAGE_HOOKS = "";
# --------------------------------------
# --------------------------------------
# this is where the repos go. If you provide a relative path (not starting
# with "/"), it's relative to your $HOME. You may want to put in something
# like "/bigdisk" or whatever if your $HOME is too small for the repos, for
# example
$REPO_BASE="/srv/git/repositories/";
# the default umask for repositories is 0077; change this if you run stuff
# like gitweb and find it can't read the repos. Please note the syntax; the
# leading 0 is required
$REPO_UMASK = 0002;
# $REPO_UMASK = 0027; # gets you 'rwxr-x---'
# $REPO_UMASK = 0022; # gets you 'rwxr-xr-x'
# part of the setup of gitweb is a variable called $projects_list (please see
# gitweb documentation for more on this). Set this to the same value:
$PROJECTS_LIST = $ENV{HOME} . "/projects.list";
# --------------------------------------
# I see no reason anyone may want to change the gitolite admin directory, but
# feel free to do so. However, please note that it *must* be an *absolute*
# path (i.e., starting with a "/" character)
# gitolite admin directory, files, etc
$GL_ADMINDIR="/etc/gitolite";
# --------------------------------------
# templates for location of the log files and format of their names
# I prefer this template (note the %y and %m placeholders)
# it produces files like `~/.gitolite/logs/gitolite-2009-09.log`
$GL_LOGT="/var/log/gitolite/gitolite-%y-%m.log";
# other choices are below, or you can make your own -- but PLEASE MAKE SURE
# the directory exists and is writable; gitolite won't do that for you (unless
# it is the default, which is "$GL_ADMINDIR/logs")
# $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m-%d.log";
# $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y.log";
# --------------------------------------
# Please DO NOT change these three paths
$GL_CONF="$GL_ADMINDIR/conf/gitolite.conf";
$GL_KEYDIR="$GL_ADMINDIR/keydir";
$GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm";
# --------------------------------------
# if git on your server is on a standard path (that is
# ssh git@server git --version
# works), leave this setting as is. Otherwise, choose one of the
# alternatives, or write your own
$GIT_PATH="";
# $GIT_PATH="/opt/bin/";
# --------------------------------------
# ----------------------------------------------------------------------
# BIG CONFIG SETTINGS
# Please read doc/big-config.mkd for details
$GL_BIG_CONFIG = 1;
$GL_NO_DAEMON_NO_GITWEB = 1;
$GL_NO_CREATE_REPOS = 1;
$GL_NO_SETUP_AUTHKEYS = 1;
# ----------------------------------------------------------------------
# SECURITY SENSITIVE SETTINGS
#
# Settings below this point may have security implications. That
# usually means that I have not thought hard enough about all the
# possible ways to crack security if these settings are enabled.
# Please see details on each setting for specifics, if any.
# ----------------------------------------------------------------------
# --------------------------------------
# ALLOW REPO ADMIN TO SET GITCONFIG KEYS
#
# Gitolite allows you to set git repo options using the "config" keyword; see
# conf/example.conf for details and syntax.
#
# However, if you are in an installation where the repo admin does not (and
# should not) have shell access to the server, then allowing him to set
# arbitrary repo config options *may* be a security risk -- some config
# settings may allow executing arbitrary commands.
#
# You have 3 choices. By default $GL_GITCONFIG_KEYS is left empty, which
# completely disables this feature (meaning you cannot set git configs from
# the repo config).
$GL_GITCONFIG_KEYS = "";
# The second choice is to give it a space separated list of settings you
# consider safe. (These are actually treated as a set of regular expression
# patterns, and any one of them must match). For example:
# $GL_GITCONFIG_KEYS = "core\.logAllRefUpdates core\..*compression";
# allows repo admins to set one of those 3 config keys (yes, that second
# pattern matches two settings from "man git-config", if you look)
#
# The third choice (which you may have guessed already if you're familiar with
# regular expressions) is to allow anything and everything:
# $GL_GITCONFIG_KEYS = ".*";
# --------------------------------------
# EXTERNAL COMMAND HELPER -- HTPASSWD
# security note: runs an external command (htpasswd) with specific arguments,
# including a user-chosen "password".
# if you want to enable the "htpasswd" command, give this the absolute path to
# whatever file apache (etc) expect to find the passwords in.
$HTPASSWD_FILE = "";
# Look in doc/3 ("easier to link gitweb authorisation with gitolite" section)
# for more details on using this feature.
# --------------------------------------
# EXTERNAL COMMAND HELPER -- RSYNC
# security note: runs an external command (rsync) with specific arguments, all
# presumably filled in correctly by the client-side rsync.
# base path of all the files that are accessible via rsync. Must be an
# absolute path. Leave it undefined or set to the empty string to disable the
# rsync helper.
$RSYNC_BASE = "";
# $RSYNC_BASE = "/home/git/up-down";
# $RSYNC_BASE = "/tmp/up-down";
# --------------------------------------
# EXTERNAL COMMAND HELPER -- SVNSERVE
# security note: runs an external command (svnserve) with specific arguments,
# as specified below. %u is substituted with the username.
# This setting allows launching svnserve when requested by the ssh client.
# This allows using the same SSH setup (hostname/username/public key) for both
# SVN and git access. Leave it undefined or set to the empty string to disable
# svnserve access.
$SVNSERVE = "";
# $SVNSERVE = "/usr/bin/svnserve -r /var/svn/ -t --tunnel-user=%u";
# --------------------------------------
# ALLOW REPO CONFIG TO USE WILDCARDS
# security note: this used to in a separate "wildrepos" branch. You can
# create repositories based on wild cards, give "ownership" to the specific
# user who created it, allow him/her to hand out R and RW permissions to other
# users to collaborate, etc. This is powerful stuff, and I've made it as
# secure as I can, but it hasn't had the kind of rigorous line-by-line
# analysis that the old "master" branch had.
# This has now been rolled into master, with all the functionality gated by
# this variable. Set this to 1 if you want to enable the wildrepos features.
# Please see doc/4-wildcard-repositories.mkd for details.
$GL_WILDREPOS = 0;
# --------------------------------------
# DEFAULT WILDCARD PERMISSIONS
# If set, this value will be used as the default user-level permission rule of
# new wildcard repositories. The user can change this value with the setperms command
# as desired after repository creation; it is only a default. Note that @all can be
# used here but is special; no other groups can be used in user-level permissions.
# $GL_WILDREPOS_DEFPERMS = 'R = @all';
# --------------------------------------
# HOOK CHAINING
# by default, the update hook in every repo chains to "update.secondary".
# Similarly, the post-update hook in the admin repo chains to
# "post-update.secondary". If you're fine with the defaults, there's no need
# to do anything here. However, if you want to use different names or paths,
# change these variables
# $UPDATE_CHAINS_TO = "hooks/update.secondary";
# $ADMIN_POST_UPDATE_CHAINS_TO = "hooks/post-update.secondary";
# --------------------------------------
# ADMIN DEFINED COMMANDS
# WARNING: Use this feature only if (a) you really really know what you're
# doing or (b) you really don't care too much about security. Please read
# doc/admin-defined-commands.mkd for details.
# $GL_ADC_PATH = "";
# --------------------------------------
# per perl rules, this should be the last line in such a file:
1;
# Local variables:
# mode: perl
# End:
# vim: set syn=perl:

195
files/gitolite3.rc Normal file
View file

@ -0,0 +1,195 @@
# configuration variables for gitolite
# This file is in perl syntax. But you do NOT need to know perl to edit it --
# just mind the commas, use single quotes unless you know what you're doing,
# and make sure the brackets and braces stay matched up!
# (Tip: perl allows a comma after the last item in a list also!)
# HELP for commands can be had by running the command with "-h".
# HELP for all the other FEATURES can be found in the documentation (look for
# "list of non-core programs shipped with gitolite" in the master index) or
# directly in the corresponding source file.
%RC = (
# ------------------------------------------------------------------
# default umask gives you perms of '0700'; see the rc file docs for
# how/why you might change this
UMASK => 0077,
# look for "git-config" in the documentation
GIT_CONFIG_KEYS => '',
# comment out if you don't need all the extra detail in the logfile
LOG_EXTRA => 1,
# syslog options
# 1. leave this section as is for normal gitolite logging
# 2. uncomment this line to log only to syslog:
# LOG_DEST => 'syslog',
# 3. uncomment this line to log to syslog and the normal gitolite log:
# LOG_DEST => 'syslog,normal',
# roles. add more roles (like MANAGER, TESTER, ...) here.
# WARNING: if you make changes to this hash, you MUST run 'gitolite
# compile' afterward, and possibly also 'gitolite trigger POST_COMPILE'
ROLES => {
READERS => 1,
WRITERS => 1,
},
# enable caching (currently only Redis). PLEASE RTFM BEFORE USING!!!
# CACHE => 'Redis',
# ------------------------------------------------------------------
# rc variables used by various features
# the 'info' command prints this as additional info, if it is set
# SITE_INFO => 'Please see http://blahblah/gitolite for more help',
# the CpuTime feature uses these
# display user, system, and elapsed times to user after each git operation
# DISPLAY_CPU_TIME => 1,
# display a warning if total CPU times (u, s, cu, cs) crosses this limit
# CPU_TIME_WARN_LIMIT => 0.1,
# the Mirroring feature needs this
# HOSTNAME => "foo",
# TTL for redis cache; PLEASE SEE DOCUMENTATION BEFORE UNCOMMENTING!
# CACHE_TTL => 600,
# ------------------------------------------------------------------
# suggested locations for site-local gitolite code (see cust.html)
# this one is managed directly on the server
# LOCAL_CODE => "$ENV{HOME}/local",
# or you can use this, which lets you put everything in a subdirectory
# called "local" in your gitolite-admin repo. For a SECURITY WARNING
# on this, see http://gitolite.com/gitolite/non-core.html#pushcode
# LOCAL_CODE => "$rc{GL_ADMIN_BASE}/local",
# ------------------------------------------------------------------
# List of commands and features to enable
ENABLE => [
# COMMANDS
# These are the commands enabled by default
'help',
'desc',
'info',
'perms',
'writable',
# Uncomment or add new commands here.
# 'create',
# 'fork',
# 'mirror',
# 'readme',
# 'sskm',
# 'D',
# These FEATURES are enabled by default.
# essential (unless you're using smart-http mode)
'ssh-authkeys',
# creates git-config enties from gitolite.conf file entries like 'config foo.bar = baz'
'git-config',
# creates git-daemon-export-ok files; if you don't use git-daemon, comment this out
'daemon',
# creates projects.list file; if you don't use gitweb, comment this out
#'gitweb',
# These FEATURES are disabled by default; uncomment to enable. If you
# need to add new ones, ask on the mailing list :-)
# user-visible behaviour
# prevent wild repos auto-create on fetch/clone
# 'no-create-on-read',
# no auto-create at all (don't forget to enable the 'create' command!)
# 'no-auto-create',
# access a repo by another (possibly legacy) name
# 'Alias',
# give some users direct shell access. See documentation in
# sts.html for details on the following two choices.
# "Shell $ENV{HOME}/.gitolite.shell-users",
# 'Shell alice bob',
# set default roles from lines like 'option default.roles-1 = ...', etc.
# 'set-default-roles',
# show more detailed messages on deny
# 'expand-deny-messages',
# show a message of the day
# 'Motd',
# system admin stuff
# enable mirroring (don't forget to set the HOSTNAME too!)
# 'Mirroring',
# allow people to submit pub files with more than one key in them
# 'ssh-authkeys-split',
# selective read control hack
# 'partial-copy',
# manage local, gitolite-controlled, copies of read-only upstream repos
# 'upstream',
# updates 'description' file instead of 'gitweb.description' config item
# 'cgit',
# allow repo-specific hooks to be added
# 'repo-specific-hooks',
# performance, logging, monitoring...
# be nice
# 'renice 10',
# log CPU times (user, system, cumulative user, cumulative system)
# 'CpuTime',
# syntactic_sugar for gitolite.conf and included files
# allow backslash-escaped continuation lines in gitolite.conf
# 'continuation-lines',
# create implicit user groups from directory names in keydir/
# 'keysubdirs-as-groups',
# allow simple line-oriented macros
# 'macros',
# Kindergarten mode
# disallow various things that sensible people shouldn't be doing anyway
# 'Kindergarten',
],
);
# ------------------------------------------------------------------------------
# per perl rules, this should be the last line in such a file:
1;
# Local variables:
# mode: perl
# End:
# vim: set syn=perl:

186
files/load_from_disk.py Normal file
View file

@ -0,0 +1,186 @@
#!/usr/bin/env python
import argparse
import requests
import os
from sqlalchemy.exc import SQLAlchemyError
if 'PAGURE_CONFIG' not in os.environ \
and os.path.exists('/etc/pagure/pagure.cfg'):
print 'Using configuration file `/etc/pagure/pagure.cfg`'
os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
import pagure
import pagure.lib
import pagure.lib.model
def get_poc_of_pkgs(debug=False):
""" Retrieve a dictionary giving the point of contact of each package
in pkgdb.
"""
if debug:
print 'Querying pkgdb'
PKGDB_URL = 'https://admin.stg.fedoraproject.org/pkgdb/api/'
req = requests.get(PKGDB_URL + 'bugzilla').text
if debug:
print 'Pkgdb data retrieved, getting POC'
pkgs = {}
for line in req.split('\n'):
line = line.strip()
if not line or line.startswith('#'):
continue
line = line.split('|')
if len(line) < 4:
continue
pkgs[line[1]] = line[3]
return pkgs
def main(folder, debug=False):
"""
Logic:
- Query the list of maintainer/PoC from pkgdb
- Browse the directory
- For each git in the directory, create the project with the correct POC
"""
pocs = get_poc_of_pkgs(debug=debug)
if debug:
print 'Adding the user to the DB'
for user in sorted(set(pocs.values())):
if debug:
print user
try:
pagure.lib.set_up_user(
session=pagure.SESSION,
username=user,
fullname=user,
default_email='%s@fedoraproject.org' % user,
keydir=pagure.APP.config.get('GITOLITE_KEYDIR', None),
)
pagure.SESSION.commit()
except SQLAlchemyError as err:
pagure.SESSION.rollback()
print 'ERROR with user %s' % user
print err
for project in sorted(os.listdir(folder)):
if debug:
print project
if not project.endswith('.git'):
if debug:
print ' -skip: not a git repository'
continue
if project.split('.git')[0] not in pocs:
if debug:
print ' -skip: no pocs'
continue
try:
name = project.split('.git')[0]
orig_name = name
name = 'rpms/%s' % name
if name in pagure.APP.config['BLACKLISTED_PROJECTS']:
raise pagure.exceptions.RepoExistsException(
'No project "%s" are allowed to be created due to potential '
'conflicts in URLs with pagure itself' % name
)
user_obj = pagure.lib.get_user(pagure.SESSION, pocs[orig_name])
allowed_prefix = pagure.APP.config[
'ALLOWED_PREFIX'] + [grp for grp in user_obj.groups]
first_part, _, second_part = name.partition('/')
if second_part and first_part not in allowed_prefix:
raise pagure.exceptions.PagureException(
'The prefix of your project must be in the list of allowed '
'prefixes set by the admins of this pagure instance, or the name '
'of a group of which you are a member.'
)
gitfolder = pagure.APP.config['GIT_FOLDER']
docfolder = pagure.APP.config['DOCS_FOLDER']
ticketfolder = pagure.APP.config['TICKETS_FOLDER']
requestfolder = pagure.APP.config['REQUESTS_FOLDER']
gitrepo = os.path.join(gitfolder, '%s.git' % name)
project = pagure.lib.model.Project(
name=name,
description=None,
url=None,
avatar_email=None,
user_id=user_obj.id,
parent_id=None,
hook_token=pagure.lib.login.id_generator(40)
)
pagure.SESSION.add(project)
# Make sure we won't have SQLAlchemy error before we create the repo
pagure.SESSION.flush()
http_clone_file = os.path.join(gitrepo, 'git-daemon-export-ok')
if not os.path.exists(http_clone_file):
with open(http_clone_file, 'w') as stream:
pass
docrepo = os.path.join(docfolder, project.path)
if os.path.exists(docrepo):
shutil.rmtree(gitrepo)
raise pagure.exceptions.RepoExistsException(
'The docs repo "%s" already exists' % project.path
)
pygit2.init_repository(docrepo, bare=True)
ticketrepo = os.path.join(ticketfolder, project.path)
if os.path.exists(ticketrepo):
shutil.rmtree(gitrepo)
shutil.rmtree(docrepo)
raise pagure.exceptions.RepoExistsException(
'The tickets repo "%s" already exists' % project.path
)
pygit2.init_repository(
ticketrepo, bare=True,
mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP)
requestrepo = os.path.join(requestfolder, project.path)
if os.path.exists(requestrepo):
shutil.rmtree(gitrepo)
shutil.rmtree(docrepo)
shutil.rmtree(ticketrepo)
raise pagure.exceptions.RepoExistsException(
'The requests repo "%s" already exists' % project.path
)
pygit2.init_repository(
requestrepo, bare=True,
mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP)
pagure.SESSION.commit()
except pagure.exceptions.PagureException as err:
print 'ERROR with project %s' % project
print err
except SQLAlchemyError as err: # pragma: no cover
pagure.SESSION.rollback()
print 'ERROR (DB) with project %s' % project
print err
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Script creating projects on pagure based on the git '
'repos present in the specified folder and the pkgdb information.'
)
parser.add_argument(
'folder',
help='Folder containing all the git repos of the projects to create')
parser.add_argument(
'--debug', dest='debug', action='store_true', default=False,
help='Print the debugging output')
args = parser.parse_args()
main(args.folder, debug=args.debug)

217
files/pagure.cfg.sample Normal file
View file

@ -0,0 +1,217 @@
import os
from datetime import timedelta
### Set the time after which the admin session expires
# There are two sessions on pagure, login that holds for 31 days and
# the session defined here after which an user has to re-login.
# This session is used when accessing all administrative parts of pagure
# (ie: changing a project's or a user's settings)
ADMIN_SESSION_LIFETIME = timedelta(minutes=20)
### Secret key for the Flask application
SECRET_KEY='<The web application secret key>'
### url to the database server:
#DB_URL=mysql://user:pass@host/db_name
#DB_URL=postgres://user:pass@host/db_name
DB_URL = 'sqlite:////var/tmp/pagure_dev.sqlite'
### The FAS group in which the admin of pagure are
ADMIN_GROUP = ['sysadmin-main']
### Hard-coded list of global admins
PAGURE_ADMIN_USERS = []
### The email address to which the flask.log will send the errors (tracebacks)
EMAIL_ERROR = 'pingou@pingoured.fr'
### SMTP settings
SMTP_SERVER = 'localhost'
SMTP_PORT = 25
SMTP_SSL = False
#Specify both for enabling SMTP with auth
SMTP_USERNAME = None
SMTP_PASSWORD = None
### Information used to sent notifications
FROM_EMAIL = 'pagure@pagure.io'
DOMAIN_EMAIL_NOTIFICATIONS = 'pagure.io'
SALT_EMAIL = '<secret key to be changed>'
### The URL at which the project is available.
APP_URL = 'https://pagure.io/'
### The URL at which the documentation of projects will be available
## This should be in a different domain to avoid XSS issues since we want
## to allow raw html to be displayed (different domain, ie not a sub-domain).
DOC_APP_URL = 'https://docs.pagure.org'
### The URL to use to clone git repositories.
GIT_URL_SSH = 'ssh://git@pagure.io/'
GIT_URL_GIT = 'git://pagure.io/'
### Folder containing to the git repos
GIT_FOLDER = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'..',
'repos'
)
### Folder containing the forks repos
FORK_FOLDER = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'..',
'forks'
)
### Folder containing the docs repos
DOCS_FOLDER = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'..',
'docs'
)
### Folder containing the tickets repos
TICKETS_FOLDER = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'..',
'tickets'
)
### Folder containing the pull-requests repos
REQUESTS_FOLDER = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'..',
'requests'
)
### Folder containing the clones for the remote pull-requests
REMOTE_GIT_FOLDER = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'..',
'remotes'
)
### Whether to enable scanning for viruses in attachments
VIRUS_SCAN_ATTACHMENTS = False
### Configuration file for gitolite
GITOLITE_CONFIG = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'..',
'gitolite.conf'
)
### Home folder of the gitolite user
### Folder where to run gl-compile-conf from
GITOLITE_HOME = None
### Version of gitolite used: 2 or 3?
GITOLITE_VERSION = 3
### Folder containing all the public ssh keys for gitolite
GITOLITE_KEYDIR = None
### Path to the gitolite.rc file
GL_RC = None
### Path to the /bin directory where the gitolite tools can be found
GL_BINDIR = None
# SSH Information
### The ssh certificates of the git server to be provided to the user
### /!\ format is important
# SSH_KEYS = {'RSA': {'fingerprint': '<foo>', 'pubkey': '<bar>'}}
# Optional configuration
### Number of items displayed per page
# Used when listing items
ITEM_PER_PAGE = 50
### Maximum size of the uploaded content
# Used to limit the size of file attached to a ticket for example
MAX_CONTENT_LENGTH = 4 * 1024 * 1024 # 4 megabytes
### Lenght for short commits ids or file hex
SHORT_LENGTH = 6
### List of blacklisted project names that can conflicts for pagure's URLs
### or other
BLACKLISTED_PROJECTS = [
'static', 'pv', 'releases', 'new', 'api', 'settings',
'logout', 'login', 'users', 'groups', 'projects']
### IP addresses allowed to access the internal endpoints
### These endpoints are used by the milter and are security sensitive, thus
### the IP filter
IP_ALLOWED_INTERNAL = ['127.0.0.1', 'localhost', '::1']
### EventSource/Web-Hook/Redis configuration
# The eventsource integration is what allows pagure to refresh the content
# on your page when someone else comments on the ticket (and this without
# asking you to reload the page.
# By default it is off, ie: EVENTSOURCE_SOURCE is None, to turn it on, specify
# here what the URL of the eventsource server is, for example:
# https://ev.pagure.io or https://pagure.io:8080 or whatever you are using
# (Note: the urls sent to it start with a '/' so no need to add one yourself)
EVENTSOURCE_SOURCE = None
# Port where the event source server is running (maybe be the same port
# as the one specified in EVENTSOURCE_SOURCE or a different one if you
# have something running in front of the server such as apache or stunnel).
EVENTSOURCE_PORT = 8080
# If this port is specified, the event source server will run another server
# at this port and will provide information about the number of active
# connections running on the first (main) event source server
#EV_STATS_PORT = 8888
# Web-hook can be turned on or off allowing using them for notifications, or
# not.
WEBHOOK = False
### Redis configuration
# A redis server is required for both the Event-Source server or the web-hook
# server.
REDIS_HOST = '0.0.0.0'
REDIS_PORT = 6379
REDIS_DB = 0
# Authentication related configuration option
### Switch the authentication method
# Specify which authentication method to use, defaults to `fas` can be or
# `local`
# Default: ``fas``.
PAGURE_AUTH = 'fas'
# When this is set to True, the session cookie will only be returned to the
# server via ssl (https). If you connect to the server via plain http, the
# cookie will not be sent. This prevents sniffing of the cookie contents.
# This may be set to False when testing your application but should always
# be set to True in production.
# Default: ``True``.
SESSION_COOKIE_SECURE = False
# The name of the cookie used to store the session id.
# Default: ``.pagure``.
SESSION_COOKIE_NAME = 'pagure'
# Boolean specifying whether to check the user's IP address when retrieving
# its session. This make things more secure (thus is on by default) but
# under certain setup it might not work (for example is there are proxies
# in front of the application).
CHECK_SESSION_IP = True
# Used by SESSION_COOKIE_PATH
APPLICATION_ROOT = '/'
# Allow the backward compatiblity endpoints for the old URLs schema to
# see the commits of a repo. This is only interesting if you pagure instance
# was running since before version 1.3 and if you care about backward
# compatibility in your URLs.
OLD_VIEW_COMMIT_ENABLED = False

114
files/pagure.conf Normal file
View file

@ -0,0 +1,114 @@
#WSGISocketPrefix run/wsgi
##WSGIRestrictStdout On
#WSGIRestrictSignal Off
#WSGIPythonOptimize 1
#WSGIPassAuthorization On
#WSGIDaemonProcess pagure user=git group=git maximum-requests=1000 display-name=pagure processes=4 threads=4 inactivity-timeout=300
## It is important that the doc server runs in a different apache process
#WSGIDaemonProcess paguredocs user=git group=git maximum-requests=1000 display-name=pagure processes=4 threads=4 inactivity-timeout=300
#<VirtualHost *:80>
#ServerName pagure.io
#Redirect permanent / https://pagure.io/
#</VirtualHost>
#<VirtualHost *:80>
#ServerName docs.pagure.org
#Redirect permanent / https://docs.pagure.org/
#</VirtualHost>
#<VirtualHost *:443>
#ServerName docs.pagure.org
#WSGIScriptAlias / /usr/share/pagure/docs_pagure.wsgi
#SSLEngine on
#SSLProtocol all -SSLv2 -SSLv3
## Use secure TLSv1.1 and TLSv1.2 ciphers
#Header always add Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
#SSLCertificateFile /etc/pki/tls/....crt
#SSLCertificateChainFile /etc/pki/tls/....intermediate.crt
#SSLCertificateKeyFile /etc/pki/tls/....key
#Alias /static /usr/lib/python2.7/site-packages/pagure/static/
#<Location />
#WSGIProcessGroup paguredocs
#<IfModule mod_authz_core.c>
## Apache 2.4
#Require all granted
#</IfModule>
#<IfModule !mod_authz_core.c>
## Apache 2.2
#Order deny,allow
#Allow from all
#</IfModule>
#</Location>
#</VirtualHost>
#<VirtualHost *:443>
#ServerName pagure.io
#WSGIScriptAlias / /usr/share/pagure/pagure.wsgi
#SSLEngine on
#SSLProtocol all -SSLv2 -SSLv3
## Use secure TLSv1.1 and TLSv1.2 ciphers
#Header always add Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
#SSLCertificateFile /etc/pki/tls/....crt
#SSLCertificateChainFile /etc/pki/tls/....intermediate.crt
#SSLCertificateKeyFile /etc/pki/tls/....key
#Alias /static /usr/lib/python2.7/site-packages/pagure/static/
#Alias /releases /var/www/releases
## Section used to support cloning git repo over http (https in this case)
#SetEnv GIT_PROJECT_ROOT /srv/git/repositories
#AliasMatch ^/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$ /path/to/git/repositories/$1
#AliasMatch ^/(.*/objects/pack/pack-[0-9a-f]{40}.(pack|idx))$ /path/to/git/repositories/$1
#ScriptAliasMatch \
#"(?x)^/(.*/(HEAD | \
#info/refs | \
#objects/info/[^/]+ | \
#git-(upload|receive)-pack))$" \
#/usr/libexec/git-core/git-http-backend/$1
#<Location />
#WSGIProcessGroup pagure
#<IfModule mod_authz_core.c>
## Apache 2.4
#Require all granted
#</IfModule>
#<IfModule !mod_authz_core.c>
## Apache 2.2
#Order deny,allow
#Allow from all
#</IfModule>
#</Location>
## Folder where are stored the tarball of the releases
#<Location /releases>
#WSGIProcessGroup pagure
#<IfModule mod_authz_core.c>
## Apache 2.4
#Require all granted
#</IfModule>
#<IfModule !mod_authz_core.c>
## Apache 2.2
#Order deny,allow
#Allow from all
#</IfModule>
#</Location>
#<Directory /var/www/releases>
#Options +Indexes
#</Directory>
#</VirtualHost>

1224
files/pagure.spec Normal file

File diff suppressed because it is too large Load diff

28
files/pagure.wsgi Normal file
View file

@ -0,0 +1,28 @@
#-*- coding: utf-8 -*-
# The three lines below are required to run on EL6 as EL6 has
# two possible version of python-sqlalchemy and python-jinja2
# These lines make sure the application uses the correct version.
import __main__
__main__.__requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4']
import pkg_resources
# Set the environment variable pointing to the configuration file
import os
os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
# Set the environment variable if the tmp folder needs to be moved
# Might be necessary to work around bug in libgit2:
# refs: https://github.com/libgit2/libgit2/issues/2965
# and https://github.com/libgit2/libgit2/issues/2797
os.environ['TEMP'] = '/var/tmp/'
# The following is only needed if you did not install pagure
# as a python module (for example if you run it from a git clone).
#import sys
#sys.path.insert(0, '/path/to/pagure/')
# The most import line to make the wsgi working
from pagure import APP as application
#application.debug = True

View file

@ -0,0 +1,247 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# Milter calls methods of your class at milter events.
# Return REJECT,TEMPFAIL,ACCEPT to short circuit processing for a message.
# You can also add/del recipients, replacebody, add/del headers, etc.
import base64
import email
import hashlib
import os
import urlparse
import StringIO
import sys
import time
from socket import AF_INET, AF_INET6
from multiprocessing import Process as Thread, Queue
import Milter
import requests
from Milter.utils import parse_addr
logq = Queue(maxsize=4)
if 'PAGURE_CONFIG' not in os.environ \
and os.path.exists('/etc/pagure/pagure.cfg'):
os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
import pagure
def get_email_body(emailobj):
''' Return the body of the email, preferably in text.
'''
body = None
if emailobj.is_multipart():
for payload in emailobj.get_payload():
body = payload.get_payload()
if payload.get_content_type() == 'text/plain':
break
else:
body = emailobj.get_payload()
enc = emailobj['Content-Transfer-Encoding']
if enc == 'base64':
body = base64.decodestring(body)
return body
def clean_item(item):
''' For an item provided as <item> return the content, if there are no
<> then return the string.
'''
if '<' in item:
item = item.split('<')[1]
if '>' in item:
item = item.split('>')[0]
return item
class PagureMilter(Milter.Base):
def __init__(self): # A new instance with each new connection.
self.id = Milter.uniqueID() # Integer incremented with each call.
self.fp = None
def log(self, message):
print(message)
sys.stdout.flush()
def envfrom(self, mailfrom, *str):
self.log("mail from: %s - %s" % (mailfrom, str))
self.fromparms = Milter.dictfromlist(str)
# NOTE: self.fp is only an *internal* copy of message data. You
# must use addheader, chgheader, replacebody to change the message
# on the MTA.
self.fp = StringIO.StringIO()
self.canon_from = '@'.join(parse_addr(mailfrom))
self.fp.write('From %s %s\n' % (self.canon_from, time.ctime()))
return Milter.CONTINUE
@Milter.noreply
def header(self, name, hval):
''' Headers '''
# add header to buffer
self.fp.write("%s: %s\n" % (name, hval))
return Milter.CONTINUE
@Milter.noreply
def eoh(self):
''' End of Headers '''
self.fp.write("\n")
return Milter.CONTINUE
@Milter.noreply
def body(self, chunk):
''' Body '''
self.fp.write(chunk)
return Milter.CONTINUE
@Milter.noreply
def envrcpt(self, to, *str):
rcptinfo = to, Milter.dictfromlist(str)
print rcptinfo
return Milter.CONTINUE
def eom(self):
''' End of Message '''
self.fp.seek(0)
msg = email.message_from_file(self.fp)
msg_id = msg.get('In-Reply-To', None)
if msg_id is None:
self.log('No In-Reply-To, keep going')
return Milter.CONTINUE
# Ensure we don't get extra lines in the message-id
msg_id = msg_id.split('\n')[0].strip()
self.log('msg-ig %s' % msg_id)
self.log('To %s' % msg['to'])
self.log('Cc %s' % msg.get('cc'))
self.log('From %s' % msg['From'])
# Ensure the user replied to his/her own notification, not that
# they are trying to forge their ID into someone else's
salt = pagure.APP.config.get('SALT_EMAIL')
m = hashlib.sha512('%s%s%s' % (msg_id, salt, clean_item(msg['From'])))
email_address = msg['to']
if 'reply+' in msg.get('cc', ''):
email_address = msg['cc']
if not 'reply+' in email_address:
self.log(
'No valid recipient email found in To/Cc: %s'
% email_address)
tohash = email_address.split('@')[0].split('+')[-1]
if m.hexdigest() != tohash:
self.log('hash: %s' % m.hexdigest())
self.log('tohash: %s' % tohash)
self.log('Hash does not correspond to the destination')
return Milter.CONTINUE
if msg['From'] and msg['From'] == pagure.APP.config.get('FROM_EMAIL'):
self.log("Let's not process the email we send")
return Milter.CONTINUE
msg_id = clean_item(msg_id)
if msg_id and '-ticket-' in msg_id:
self.log('Processing issue')
return self.handle_ticket_email(msg, msg_id)
elif msg_id and '-pull-request-' in msg_id:
self.log('Processing pull-request')
return self.handle_request_email(msg, msg_id)
else:
self.log('Not a pagure ticket or pull-request email, let it go')
return Milter.CONTINUE
def handle_ticket_email(self, emailobj, msg_id):
''' Add the email as a comment on a ticket. '''
uid = msg_id.split('-ticket-')[-1].split('@')[0]
parent_id = None
if '-' in uid:
uid, parent_id = uid.rsplit('-', 1)
if '/' in uid:
uid = uid.split('/')[0]
self.log('uid %s' % uid)
self.log('parent_id %s' % parent_id)
data = {
'objid': uid,
'comment': get_email_body(emailobj),
'useremail': clean_item(emailobj['From']),
}
url = pagure.APP.config.get('APP_URL')
if url.endswith('/'):
url = url[:-1]
url = '%s/pv/ticket/comment/' % url
req = requests.put(url, data=data)
if req.status_code == 200:
self.log('Comment added')
return Milter.ACCEPT
self.log('Could not add the comment to pagure')
return Milter.CONTINUE
def handle_request_email(self, emailobj, msg_id):
''' Add the email as a comment on a request. '''
uid = msg_id.split('-pull-request-')[-1].split('@')[0]
parent_id = None
if '-' in uid:
uid, parent_id = uid.rsplit('-', 1)
if '/' in uid:
uid = uid.split('/')[0]
self.log('uid %s' % uid)
self.log('parent_id %s' % parent_id)
data = {
'objid': uid,
'comment': get_email_body(emailobj),
'useremail': clean_item(emailobj['From']),
}
url = pagure.APP.config.get('APP_URL')
if url.endswith('/'):
url = url[:-1]
url = '%s/pv/pull-request/comment/' % url
req = requests.put(url, data=data)
return Milter.ACCEPT
def background():
while True:
t = logq.get()
if not t: break
msg,id,ts = t
print "%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S',time.localtime(ts)),id),
# 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
for i in msg: print i,
print
def main():
bt = Thread(target=background)
bt.start()
socketname = "/var/run/pagure/paguresock"
timeout = 600
# Register to have the Milter factory create instances of your class:
Milter.factory = PagureMilter
print "%s pagure milter startup" % time.strftime('%Y%b%d %H:%M:%S')
sys.stdout.flush()
Milter.runmilter("paguremilter", socketname, timeout)
logq.put(None)
bt.join()
print "%s pagure milter shutdown" % time.strftime('%Y%b%d %H:%M:%S')
if __name__ == "__main__":
main()

View file

@ -0,0 +1 @@
d /var/run/pagure 0755 postfix postfix

View file

@ -0,0 +1,14 @@
[Unit]
Description=Pagure SMTP filter (Milter) Daemon (talk to postfix over a socket)
After=postfix.target
Documentation=https://github.com/pypingou/pagure
[Service]
ExecStart=/usr/bin/python2 /usr/share/pagure/comment_email_milter.py
Type=simple
User=postfix
Group=postfix
Restart=on-failure
[Install]
WantedBy=multi-user.target

13
pagure-ci/README.rst Normal file
View file

@ -0,0 +1,13 @@
Pagure CI
=========
This is to setup Pagure CI for development. It is assumed that all the
dependencies are resolved.
* Run::
PAGURE_CONFIG=/path/to/config PYTHONPATH=. python pagure-ci/pagure_ci_server.py
Check `doc/usage/pagure_ci.rst` for further information on how to set up
and configure your project on both pagure and the CI services

View file

@ -0,0 +1,14 @@
[Unit]
Description=Pagure Continuous Integration service
After=redis.target
Documentation=https://pagure.io/pagure
[Service]
ExecStart=/usr/libexec/pagure-ci/pagure_ci_server.py
Type=simple
User=git
Group=git
Restart=on-failure
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,144 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
(c) 2016 - Copyright Red Hat Inc
Authors:
Pierre-Yves Chibon <pingou@pingoured.fr>
This server listens to message sent via redis and send the corresponding
web-hook request.
Using this mechanism, we no longer block the main application if the
receiving end is offline or so.
"""
import json
import logging
logging.basicConfig(level=logging.DEBUG)
import os
import requests
import trollius
import trollius_redis
LOG = logging.getLogger(__name__)
if 'PAGURE_CONFIG' not in os.environ \
and os.path.exists('/etc/pagure/pagure.cfg'):
print 'Using configuration file `/etc/pagure/pagure.cfg`'
os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
import pagure
import pagure.lib
@trollius.coroutine
def handle_messages():
''' Handles connecting to redis and acting upon messages received.
In this case, it means triggering a build on jenkins based on the
information provided.
'''
host = pagure.APP.config.get('REDIS_HOST', '0.0.0.0')
port = pagure.APP.config.get('REDIS_PORT', 6379)
dbname = pagure.APP.config.get('REDIS_DB', 0)
connection = yield trollius.From(trollius_redis.Connection.create(
host=host, port=port, db=dbname))
# Create subscriber.
subscriber = yield trollius.From(connection.start_subscribe())
# Subscribe to channel.
yield trollius.From(subscriber.subscribe(['pagure.ci']))
# Inside a while loop, wait for incoming events.
while True:
reply = yield trollius.From(subscriber.next_published())
LOG.info(
'Received: %s on channel: %s',
repr(reply.value), reply.channel)
data = json.loads(reply.value)
pr_id = data['pr']['id']
pr_uid = data['pr']['uid']
branch = data['pr']['branch_from']
LOG.info('Looking for PR: %s', pr_uid)
request = pagure.lib.get_request_by_uid(pagure.SESSION, pr_uid)
if not request:
LOG.warning(
'No request could be found from the message %s', data)
continue
LOG.info(
"Trigger on %s PR #%s from %s: %s",
request.project.fullname, pr_id,
request.project_from.fullname, branch)
url = request.project.ci_hook.ci_url.rstrip('/')
if data['ci_type'] == 'jenkins':
url = url + '/buildWithParameters'
repo = '%s/%s' % (
pagure.APP.config['GIT_URL_GIT'].rstrip('/'),
request.project_from.path)
LOG.info(
'Triggering the build at: %s, for repo: %s', url, repo)
requests.post(
url,
data={
'token': request.project.ci_hook.pagure_ci_token,
'cause': pr_id,
'REPO': repo,
'BRANCH': branch
}
)
else:
LOG.warning('Un-supported CI type')
LOG.info('Ready for another')
def main():
''' Start the main async loop. '''
try:
loop = trollius.get_event_loop()
tasks = [
trollius.async(handle_messages()),
]
loop.run_until_complete(trollius.wait(tasks))
loop.run_forever()
except KeyboardInterrupt:
pass
except trollius.ConnectionResetError:
pass
LOG.info("End Connection")
loop.close()
LOG.info("End")
if __name__ == '__main__':
formatter = logging.Formatter(
"%(asctime)s %(levelname)s [%(module)s:%(lineno)d] %(message)s")
# setup console logging
LOG.setLevel(logging.DEBUG)
shellhandler = logging.StreamHandler()
shellhandler.setLevel(logging.DEBUG)
aslog = logging.getLogger("asyncio")
aslog.setLevel(logging.DEBUG)
aslog = logging.getLogger("trollius")
aslog.setLevel(logging.DEBUG)
shellhandler.setFormatter(formatter)
LOG.addHandler(shellhandler)
main()

11
pagure.egg-info/PKG-INFO Normal file
View file

@ -0,0 +1,11 @@
Metadata-Version: 1.1
Name: pagure
Version: 2.6
Summary: A light-weight git-centered forge based on pygit2..
Home-page: https://fedorahosted.org/pagure/
Author: Pierre-Yves Chibon
Author-email: pingou@pingoured.fr
License: GPLv2+
Download-URL: https://fedorahosted.org/releases/p/r/pagure/
Description: UNKNOWN
Platform: UNKNOWN

408
pagure.egg-info/SOURCES.txt Normal file
View file

@ -0,0 +1,408 @@
LICENSE
MANIFEST.in
README.rst
UPGRADING.rst
createdb.py
requirements.txt
setup.py
alembic/env.py
alembic/script.py.mako
alembic/versions/15ea3c2cf83d_pr_comment_editing.py
alembic/versions/1640c7d75e5f_add_reports_field_to_project.py
alembic/versions/1b6d7dc5600a_versioning_passwords.py
alembic/versions/1cd0a853c697_add_closed_at_field_in_pr.py
alembic/versions/1d18843a1994_add_is_fork_column_to_projects.py
alembic/versions/1f3de3853a1a_add_the_tree_id_column_to_pr_inline_.py
alembic/versions/22db0a833d35_add_notifications_to_tickets.py
alembic/versions/257a7ce22682_add_the_remote_git_entry.py
alembic/versions/298891e63039_change_the_status_of_pull_requests.py
alembic/versions/2aa7b3958bc5_add_the_milestones_column.py
alembic/versions/317a285e04a8_delete_hooks.py
alembic/versions/32d636cb5e00_display_name_in_groups.py
alembic/versions/350efb3f6baf_add_namespace_to_project.py
alembic/versions/36116bb7a69b_add_the_url_field_to_project.py
alembic/versions/36386a60b3fd_add_milestone_to_issues.py
alembic/versions/368fd931cf7f_add_the_notifications_column_to_projects.py
alembic/versions/3b441ef4e928_comment_editing_issue.py
alembic/versions/3c25e14b855b_add_an_avatar_email_for_project.py
alembic/versions/43df5e588a87_add_closed_at_attribute_in_issues.py
alembic/versions/443e090da188_up_to_255_characters_for_project_name.py
alembic/versions/496f7a700f2e_add_priorities.py
alembic/versions/4cae55a80a42_add_the_initial_comment_on_the_pr_table.py
alembic/versions/58e60d869326_add_notification_bool_to_pr.py
alembic/versions/6190226bed0_add_the_updated_on_column_to_pull_.py
alembic/versions/abc71fd60fa_add_the_closed_by_column_to_pull_.py
alembic/versions/b5efae6bb23_add_merge_status_to_the_pull_requests_.py
doc/Makefile
doc/api.rst
doc/conf.py
doc/configuration.rst
doc/contributing.rst
doc/contributors.rst
doc/development.rst
doc/index.rst
doc/install.rst
doc/install_evs.rst
doc/install_milter.rst
doc/install_pagure_ci.rst
doc/install_webhooks.rst
doc/milter.rst
doc/overview.ascii
doc/overview.rst
doc/overview_simple.ascii
doc/requirements.txt
doc/usage.rst
doc/_static/overview.png
doc/_static/overview_simple.png
doc/_static/pagure.png
doc/_static/site.css
doc/_templates/pagure-logo.html
doc/usage/first_steps.rst
doc/usage/pagure_ci.rst
doc/usage/pagure_ci_jenkins.rst
doc/usage/pr_custom_page.rst
doc/usage/project_settings.rst
doc/usage/roadmap.rst
doc/usage/theming.rst
doc/usage/ticket_templates.rst
doc/usage/upgrade_db.rst
doc/usage/using_doc.rst
doc/usage/using_webhooks.rst
doc/usage/_static/pagure_add_ssh_key.png
doc/usage/_static/pagure_custom_pr.png
doc/usage/_static/pagure_my_settings.png
doc/usage/_static/pagure_roadmap2.png
doc/usage/_static/pagure_ticket_template.png
ev-server/pagure-stream-server.py
ev-server/pagure_ev.service
files/alembic.ini
files/api_key_expire_mail.py
files/doc_pagure.wsgi
files/emoji_clean_json.py
files/gitolite.rc
files/gitolite3.rc
files/load_from_disk.py
files/pagure.cfg.sample
files/pagure.conf
files/pagure.spec
files/pagure.wsgi
milters/comment_email_milter.py
milters/milter_tempfile.conf
milters/pagure_milter.service
pagure/__init__.py
pagure/default_config.py
pagure/doc_utils.py
pagure/docs_server.py
pagure/exceptions.py
pagure/forms.py
pagure/login_forms.py
pagure/mail_logging.py
pagure/pfmarkdown.py
pagure/proxy.py
pagure-ci/README.rst
pagure-ci/pagure_ci.service
pagure-ci/pagure_ci_server.py
pagure.egg-info/PKG-INFO
pagure.egg-info/SOURCES.txt
pagure.egg-info/dependency_links.txt
pagure.egg-info/entry_points.txt
pagure.egg-info/requires.txt
pagure.egg-info/top_level.txt
pagure/api/__init__.py
pagure/api/fork.py
pagure/api/issue.py
pagure/api/project.py
pagure/api/user.py
pagure/api/ci/__init__.py
pagure/api/ci/jenkins.py
pagure/doc/api.rst
pagure/hooks/__init__.py
pagure/hooks/fedmsg.py
pagure/hooks/irc.py
pagure/hooks/mail.py
pagure/hooks/pagure_ci.py
pagure/hooks/pagure_force_commit.py
pagure/hooks/pagure_hook.py
pagure/hooks/pagure_request_hook.py
pagure/hooks/pagure_ticket_hook.py
pagure/hooks/pagure_unsigned_commits.py
pagure/hooks/rtd.py
pagure/hooks/files/fedmsg_hook.py
pagure/hooks/files/git_multimail.py
pagure/hooks/files/pagure_block_unsigned.py
pagure/hooks/files/pagure_force_commit_hook.py
pagure/hooks/files/pagure_hook.py
pagure/hooks/files/pagure_hook_requests.py
pagure/hooks/files/pagure_hook_tickets.py
pagure/hooks/files/post-receive
pagure/hooks/files/pre-receive
pagure/hooks/files/rtd_hook.py
pagure/internal/__init__.py
pagure/lib/__init__.py
pagure/lib/git.py
pagure/lib/lib_ci.py
pagure/lib/link.py
pagure/lib/login.py
pagure/lib/model.py
pagure/lib/notify.py
pagure/lib/repo.py
pagure/static/favicon.ico
pagure/static/issue_ev.js
pagure/static/jdenticon-1.3.2.js
pagure/static/jdenticon-1.3.2.min.js
pagure/static/jdenticon.js
pagure/static/jdenticon.min.js
pagure/static/jquery-1.10.2.js
pagure/static/jquery-1.10.2.min.js
pagure/static/jquery-ui-1.12.0.js
pagure/static/jquery-ui-1.12.0.min.js
pagure/static/jquery-ui.js
pagure/static/jquery-ui.min.js
pagure/static/jquery.dotdotdot-1.8.3.js
pagure/static/jquery.dotdotdot-1.8.3.min.js
pagure/static/jquery.dotdotdot.js
pagure/static/jquery.dotdotdot.min.js
pagure/static/jquery.js
pagure/static/jquery.min.js
pagure/static/pagure-logo.png
pagure/static/pagure.css
pagure/static/request_ev.js
pagure/static/selectize-0.12.3.js
pagure/static/selectize-0.12.3.min.js
pagure/static/selectize.bootstrap3-0.12.3.css
pagure/static/selectize.bootstrap3.css
pagure/static/selectize.js
pagure/static/selectize.min.js
pagure/static/stupidtable-1.0.1.js
pagure/static/stupidtable-1.0.1.min.js
pagure/static/stupidtable.js
pagure/static/stupidtable.min.js
pagure/static/toggle.css
pagure/static/upload.js
pagure/static/atwho/jquery.atwho-1.5.1.css
pagure/static/atwho/jquery.atwho-1.5.1.js
pagure/static/atwho/jquery.atwho-1.5.1.min.css
pagure/static/atwho/jquery.atwho-1.5.1.min.js
pagure/static/atwho/jquery.atwho.css
pagure/static/atwho/jquery.atwho.js
pagure/static/atwho/jquery.atwho.min.css
pagure/static/atwho/jquery.atwho.min.js
pagure/static/atwho/jquery.caret-0.3.1.js
pagure/static/atwho/jquery.caret-0.3.1.min.js
pagure/static/atwho/jquery.caret.js
pagure/static/atwho/jquery.caret.min.js
pagure/static/codemirror/codemirror-5.18.2.css
pagure/static/codemirror/codemirror-5.18.2.js
pagure/static/codemirror/codemirror.css
pagure/static/codemirror/codemirror.js
pagure/static/codemirror/solarized-5.18.2.css
pagure/static/codemirror/solarized.css
pagure/static/emoji/emoji_strategy-2.2.6.json
pagure/static/emoji/emoji_strategy.json
pagure/static/emoji/emojicomplete.js
pagure/static/emoji/emojione-2.2.6.js
pagure/static/emoji/emojione-2.2.6.min.js
pagure/static/emoji/emojione.js
pagure/static/emoji/emojione.min.js
pagure/static/emoji/emojione.sprites-2.2.6.css
pagure/static/emoji/emojione.sprites-2.2.6.png
pagure/static/emoji/emojione.sprites.css
pagure/static/emoji/emojione.sprites.png
pagure/static/emoji/jquery.textcomplete-1.7.1.js
pagure/static/emoji/jquery.textcomplete-1.7.1.min.js
pagure/static/emoji/jquery.textcomplete.js
pagure/static/emoji/jquery.textcomplete.min.js
pagure/static/fonts/fonts.css
pagure/static/fonts/open-sans-v13-latin_latin-ext-300.eot
pagure/static/fonts/open-sans-v13-latin_latin-ext-300.svg
pagure/static/fonts/open-sans-v13-latin_latin-ext-300.ttf
pagure/static/fonts/open-sans-v13-latin_latin-ext-300.woff
pagure/static/fonts/open-sans-v13-latin_latin-ext-300.woff2
pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.eot
pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.svg
pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.ttf
pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.woff
pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.woff2
pagure/static/fonts/open-sans-v13-latin_latin-ext-700.eot
pagure/static/fonts/open-sans-v13-latin_latin-ext-700.svg
pagure/static/fonts/open-sans-v13-latin_latin-ext-700.ttf
pagure/static/fonts/open-sans-v13-latin_latin-ext-700.woff
pagure/static/fonts/open-sans-v13-latin_latin-ext-700.woff2
pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.eot
pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.svg
pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.ttf
pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.woff
pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.woff2
pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.eot
pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.svg
pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.ttf
pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.woff
pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.woff2
pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.eot
pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.svg
pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.ttf
pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.woff
pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.woff2
pagure/static/hack_fonts/css/hack-extended-2.020.css
pagure/static/hack_fonts/css/hack-extended-2.020.min.css
pagure/static/hack_fonts/css/hack-extended.css
pagure/static/hack_fonts/css/hack-extended.min.css
pagure/static/hack_fonts/fonts/eot/hack-bold-webfont.eot
pagure/static/hack_fonts/fonts/eot/hack-bolditalic-webfont.eot
pagure/static/hack_fonts/fonts/eot/hack-italic-webfont.eot
pagure/static/hack_fonts/fonts/eot/hack-regular-webfont.eot
pagure/static/hack_fonts/fonts/eot/latin/hack-bold-latin-webfont.eot
pagure/static/hack_fonts/fonts/eot/latin/hack-bolditalic-latin-webfont.eot
pagure/static/hack_fonts/fonts/eot/latin/hack-italic-latin-webfont.eot
pagure/static/hack_fonts/fonts/eot/latin/hack-regular-latin-webfont.eot
pagure/static/hack_fonts/fonts/svg/hack-bold-webfont.svg
pagure/static/hack_fonts/fonts/svg/hack-bolditalic-webfont.svg
pagure/static/hack_fonts/fonts/svg/hack-italic-webfont.svg
pagure/static/hack_fonts/fonts/svg/hack-regular-webfont.svg
pagure/static/hack_fonts/fonts/svg/latin/hack-bold-latin-webfont.svg
pagure/static/hack_fonts/fonts/svg/latin/hack-bolditalic-latin-webfont.svg
pagure/static/hack_fonts/fonts/svg/latin/hack-italic-latin-webfont.svg
pagure/static/hack_fonts/fonts/svg/latin/hack-regular-latin-webfont.svg
pagure/static/hack_fonts/fonts/web-ttf/hack-bold-webfont.ttf
pagure/static/hack_fonts/fonts/web-ttf/hack-bolditalic-webfont.ttf
pagure/static/hack_fonts/fonts/web-ttf/hack-italic-webfont.ttf
pagure/static/hack_fonts/fonts/web-ttf/hack-regular-webfont.ttf
pagure/static/hack_fonts/fonts/web-ttf/latin/hack-bold-latin-webfont.ttf
pagure/static/hack_fonts/fonts/web-ttf/latin/hack-bolditalic-latin-webfont.ttf
pagure/static/hack_fonts/fonts/web-ttf/latin/hack-italic-latin-webfont.ttf
pagure/static/hack_fonts/fonts/web-ttf/latin/hack-regular-latin-webfont.ttf
pagure/static/hack_fonts/fonts/woff/hack-bold-webfont.woff
pagure/static/hack_fonts/fonts/woff/hack-bolditalic-webfont.woff
pagure/static/hack_fonts/fonts/woff/hack-italic-webfont.woff
pagure/static/hack_fonts/fonts/woff/hack-regular-webfont.woff
pagure/static/hack_fonts/fonts/woff/latin/hack-bold-latin-webfont.woff
pagure/static/hack_fonts/fonts/woff/latin/hack-bolditalic-latin-webfont.woff
pagure/static/hack_fonts/fonts/woff/latin/hack-italic-latin-webfont.woff
pagure/static/hack_fonts/fonts/woff/latin/hack-regular-latin-webfont.woff
pagure/static/hack_fonts/fonts/woff2/hack-bold-webfont.woff2
pagure/static/hack_fonts/fonts/woff2/hack-bolditalic-webfont.woff2
pagure/static/hack_fonts/fonts/woff2/hack-italic-webfont.woff2
pagure/static/hack_fonts/fonts/woff2/hack-regular-webfont.woff2
pagure/static/hack_fonts/fonts/woff2/latin/hack-bold-latin-webfont.woff2
pagure/static/hack_fonts/fonts/woff2/latin/hack-bolditalic-latin-webfont.woff2
pagure/static/hack_fonts/fonts/woff2/latin/hack-italic-latin-webfont.woff2
pagure/static/hack_fonts/fonts/woff2/latin/hack-regular-latin-webfont.woff2
pagure/static/images/link.png
pagure/static/images/spinner.gif
pagure/static/images/users.png
pagure/static/open-iconic/css/open-iconic-1.1.1.css
pagure/static/open-iconic/css/open-iconic-1.1.1.min.css
pagure/static/open-iconic/css/open-iconic.css
pagure/static/open-iconic/css/open-iconic.min.css
pagure/static/open-iconic/fonts/open-iconic.eot
pagure/static/open-iconic/fonts/open-iconic.otf
pagure/static/open-iconic/fonts/open-iconic.svg
pagure/static/open-iconic/fonts/open-iconic.ttf
pagure/static/open-iconic/fonts/open-iconic.woff
pagure/templates/_browseheader.html
pagure/templates/_formhelper.html
pagure/templates/_render_repo.html
pagure/templates/activity.html
pagure/templates/add_group.html
pagure/templates/add_group_project.html
pagure/templates/add_token.html
pagure/templates/add_user.html
pagure/templates/admin_index.html
pagure/templates/api.html
pagure/templates/comment_update.html
pagure/templates/commit.html
pagure/templates/commits.html
pagure/templates/doc_ssh_keys.html
pagure/templates/docs.html
pagure/templates/edit_file.html
pagure/templates/edit_group.html
pagure/templates/edit_tag.html
pagure/templates/fatal_error.html
pagure/templates/file.html
pagure/templates/forks.html
pagure/templates/group_info.html
pagure/templates/group_list.html
pagure/templates/index.html
pagure/templates/index_auth.html
pagure/templates/issue.html
pagure/templates/issues.html
pagure/templates/master.html
pagure/templates/new_issue.html
pagure/templates/new_project.html
pagure/templates/new_release.html
pagure/templates/not_found.html
pagure/templates/plugin.html
pagure/templates/pull_request.html
pagure/templates/pull_request_comment.html
pagure/templates/pull_request_title.html
pagure/templates/releases.html
pagure/templates/remote_pull_request.html
pagure/templates/repo_info.html
pagure/templates/repo_master.html
pagure/templates/requests.html
pagure/templates/roadmap.html
pagure/templates/settings.html
pagure/templates/unauthorized.html
pagure/templates/user_emails.html
pagure/templates/user_info.html
pagure/templates/user_list.html
pagure/templates/user_requests.html
pagure/templates/user_settings.html
pagure/templates/login/login.html
pagure/templates/login/password_change.html
pagure/templates/login/password_recover.html
pagure/templates/login/password_reset.html
pagure/templates/login/user_new.html
pagure/ui/__init__.py
pagure/ui/admin.py
pagure/ui/app.py
pagure/ui/filters.py
pagure/ui/fork.py
pagure/ui/groups.py
pagure/ui/issues.py
pagure/ui/login.py
pagure/ui/plugins.py
pagure/ui/repo.py
tests/__init__.py
tests/placebo.png
tests/test_config
tests/test_pagure_flask_api.py
tests/test_pagure_flask_api_auth.py
tests/test_pagure_flask_api_fork.py
tests/test_pagure_flask_api_issue.py
tests/test_pagure_flask_api_project.py
tests/test_pagure_flask_docs.py
tests/test_pagure_flask_dump_load_ticket.py
tests/test_pagure_flask_internal.py
tests/test_pagure_flask_ui_admin.py
tests/test_pagure_flask_ui_app.py
tests/test_pagure_flask_ui_fork.py
tests/test_pagure_flask_ui_groups.py
tests/test_pagure_flask_ui_issues.py
tests/test_pagure_flask_ui_login.py
tests/test_pagure_flask_ui_no_master_branch.py
tests/test_pagure_flask_ui_plugins.py
tests/test_pagure_flask_ui_plugins_fedmsg.py
tests/test_pagure_flask_ui_plugins_irc.py
tests/test_pagure_flask_ui_plugins_mail.py
tests/test_pagure_flask_ui_plugins_noff.py
tests/test_pagure_flask_ui_plugins_pagure_ci.py
tests/test_pagure_flask_ui_plugins_pagure_hook.py
tests/test_pagure_flask_ui_plugins_pagure_request_hook.py
tests/test_pagure_flask_ui_plugins_pagure_ticket_hook.py
tests/test_pagure_flask_ui_plugins_rtd_hook.py
tests/test_pagure_flask_ui_plugins_unsigned.py
tests/test_pagure_flask_ui_priorities.py
tests/test_pagure_flask_ui_repo.py
tests/test_pagure_flask_ui_repo_slash_name.py
tests/test_pagure_flask_ui_roadmap.py
tests/test_pagure_flask_ui_slash_branch_name.py
tests/test_pagure_lib.py
tests/test_pagure_lib_git.py
tests/test_pagure_lib_git_get_tags_objects.py
tests/test_pagure_lib_link.py
tests/test_pagure_lib_login.py
tests/test_pagure_lib_model.py
tests/test_zzz_pagure_flask_ui_old_commit.py
webhook-server/pagure-webhook-server.py
webhook-server/pagure_webhook.service

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,4 @@
[moksha.consumer]
integrator = pagureCI.consumer:Integrator

View file

@ -0,0 +1,32 @@
alembic
arrow
binaryornot
bleach
blinker
chardet
docutils
enum34
flask
flask-wtf
flask-multistatic
kitchen
markdown
munch
Pillow
psutil
pyclamd
pygit2 >= 0.20.1
pygments
python-openid
python-openid-cla
python-openid-teams
redis
six
sqlalchemy >= 0.8
straight.plugin==1.4.0-post-1
trollius-redis
wtforms
cryptography
python-fedora
py-bcrypt
python-jenkins

View file

@ -0,0 +1 @@
pagure

620
pagure/__init__.py Normal file
View file

@ -0,0 +1,620 @@
# -*- coding: utf-8 -*-
"""
(c) 2014-2015 - Copyright Red Hat Inc
Authors:
Pierre-Yves Chibon <pingou@pingoured.fr>
"""
# These two lines are needed to run on EL6
__requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4']
import pkg_resources
__version__ = '2.6'
__api_version__ = '0.7'
import datetime
import logging
import os
import subprocess
import urlparse
from logging.handlers import SMTPHandler
import flask
import pygit2
import werkzeug
from functools import wraps
from sqlalchemy.exc import SQLAlchemyError
from pygments import highlight
from pygments.lexers.text import DiffLexer
from pygments.formatters import HtmlFormatter
from flask_multistatic import MultiStaticFlask
from werkzeug.routing import BaseConverter
import pagure.exceptions
# Create the application.
APP = MultiStaticFlask(__name__)
APP.jinja_env.trim_blocks = True
APP.jinja_env.lstrip_blocks = True
# set up FAS
APP.config.from_object('pagure.default_config')
if 'PAGURE_CONFIG' in os.environ:
APP.config.from_envvar('PAGURE_CONFIG')
if APP.config.get('THEME_TEMPLATE_FOLDER', False):
# Jinja can be told to look for templates in different folders
# That's what we do here
template_folder = APP.config['THEME_TEMPLATE_FOLDER']
if template_folder[0] != '/':
template_folder = os.path.join(
APP.root_path, APP.template_folder, template_folder)
import jinja2
# Jinja looks for the template in the order of the folders specified
templ_loaders = [
jinja2.FileSystemLoader(template_folder),
APP.jinja_loader,
]
APP.jinja_loader = jinja2.ChoiceLoader(templ_loaders)
if APP.config.get('THEME_STATIC_FOLDER', False):
static_folder = APP.config['THEME_STATIC_FOLDER']
if static_folder[0] != '/':
static_folder = os.path.join(
APP.root_path, 'static', static_folder)
# Unlike templates, to serve static files from multiples folders we
# need flask-multistatic
APP.static_folder = [
static_folder,
os.path.join(APP.root_path, 'static'),
]
import pagure.doc_utils
import pagure.forms
import pagure.lib
import pagure.lib.git
import pagure.login_forms
import pagure.mail_logging
import pagure.proxy
# Only import flask_fas_openid if it is needed
if APP.config.get('PAGURE_AUTH', None) in ['fas', 'openid']:
from flask_fas_openid import FAS
FAS = FAS(APP)
@FAS.postlogin
def set_user(return_url):
''' After login method. '''
try:
pagure.lib.set_up_user(
session=SESSION,
username=flask.g.fas_user.username,
fullname=flask.g.fas_user.fullname,
default_email=flask.g.fas_user.email,
ssh_key=flask.g.fas_user.get('ssh_key'),
keydir=APP.config.get('GITOLITE_KEYDIR', None),
)
# If groups are managed outside pagure, set up the user at login
if not APP.config.get('ENABLE_GROUP_MNGT', False):
user = pagure.lib.search_user(
SESSION, username=flask.g.fas_user.username)
groups = set(user.groups)
fas_groups = set(flask.g.fas_user.groups)
# Add the new groups
for group in fas_groups - groups:
group = pagure.lib.search_groups(
SESSION, group_name=group)
if not group:
continue
try:
pagure.lib.add_user_to_group(
session=SESSION,
username=flask.g.fas_user.username,
group=group,
user=flask.g.fas_user.username,
is_admin=is_admin(),
)
except pagure.exceptions.PagureException as err:
LOG.debug(err)
# Remove the old groups
for group in groups - fas_groups:
try:
pagure.lib.delete_user_of_group(
session=SESSION,
username=flask.g.fas_user.username,
groupname=group,
user=flask.g.fas_user.username,
is_admin=is_admin(),
force=True,
)
except pagure.exceptions.PagureException as err:
LOG.debug(err)
SESSION.commit()
except SQLAlchemyError as err:
SESSION.rollback()
LOG.debug(err)
LOG.exception(err)
flask.flash(
'Could not set up you as a user properly, please contact '
'an admin', 'error')
return flask.redirect(return_url)
SESSION = pagure.lib.create_session(APP.config['DB_URL'])
REDIS = None
if APP.config['EVENTSOURCE_SOURCE'] \
or APP.config['WEBHOOK'] \
or APP.config['PAGURE_CI_SERVICES']:
pagure.lib.set_redis(
host=APP.config['REDIS_HOST'],
port=APP.config['REDIS_PORT'],
dbname=APP.config['REDIS_DB']
)
if APP.config['PAGURE_CI_SERVICES']:
pagure.lib.set_pagure_ci(APP.config['PAGURE_CI_SERVICES'])
if not APP.debug:
APP.logger.addHandler(pagure.mail_logging.get_mail_handler(
smtp_server=APP.config.get('SMTP_SERVER', '127.0.0.1'),
mail_admin=APP.config.get('MAIL_ADMIN', APP.config['EMAIL_ERROR'])
))
# Send classic logs into syslog
SHANDLER = logging.StreamHandler()
SHANDLER.setLevel(APP.config.get('LOG_LEVEL', 'INFO'))
APP.logger.addHandler(SHANDLER)
LOG = APP.logger
LOG.setLevel(APP.config.get('LOG_LEVEL', 'INFO'))
APP.wsgi_app = pagure.proxy.ReverseProxied(APP.wsgi_app)
def authenticated():
''' Utility function checking if the current user is logged in or not.
'''
return hasattr(flask.g, 'fas_user') and flask.g.fas_user is not None
def logout():
auth = APP.config.get('PAGURE_AUTH', None)
if auth in ['fas', 'openid']:
if hasattr(flask.g, 'fas_user') and flask.g.fas_user is not None:
FAS.logout()
elif auth == 'local':
import pagure.ui.login as login
login.logout()
def api_authenticated():
''' Utility function checking if the current user is logged in or not
in the API.
'''
return hasattr(flask.g, 'fas_user') \
and flask.g.fas_user is not None \
and hasattr(flask.g, 'token') \
and flask.g.token is not None
def admin_session_timedout():
''' Check if the current user has been authenticated for more than what
is allowed (defaults to 15 minutes).
If it is the case, the user is logged out and the method returns True,
otherwise it returns False.
'''
timedout = False
if not authenticated():
return True
login_time = flask.g.fas_user.login_time
# This is because flask_fas_openid will store this as a posix timestamp
if not isinstance(login_time, datetime.datetime):
login_time = datetime.datetime.utcfromtimestamp(login_time)
if (datetime.datetime.utcnow() - login_time) > \
APP.config.get('ADMIN_SESSION_LIFETIME',
datetime.timedelta(minutes=15)):
timedout = True
logout()
return timedout
def is_safe_url(target): # pragma: no cover
""" Checks that the target url is safe and sending to the current
website not some other malicious one.
"""
ref_url = urlparse.urlparse(flask.request.host_url)
test_url = urlparse.urlparse(
urlparse.urljoin(flask.request.host_url, target))
return test_url.scheme in ('http', 'https') and \
ref_url.netloc == test_url.netloc
def is_admin():
""" Return whether the user is admin for this application or not. """
if not authenticated():
return False
user = flask.g.fas_user
auth_method = APP.config.get('PAGURE_AUTH', None)
if auth_method == 'fas':
if not user.cla_done or len(user.groups) < 1:
return False
admin_users = APP.config.get('PAGURE_ADMIN_USERS', [])
if not isinstance(admin_users, list):
admin_users = [admin_users]
if user.username in admin_users:
return True
admins = APP.config['ADMIN_GROUP']
if isinstance(admins, basestring):
admins = [admins]
admins = set(admins or [])
groups = set(flask.g.fas_user.groups)
return not groups.isdisjoint(admins)
def is_repo_admin(repo_obj):
""" Return whether the user is an admin of the provided repo. """
if not authenticated():
return False
user = flask.g.fas_user.username
admin_users = APP.config.get('PAGURE_ADMIN_USERS', [])
if not isinstance(admin_users, list):
admin_users = [admin_users]
if user in admin_users:
return True
usergrps = [
usr.user
for grp in repo_obj.groups
for usr in grp.users]
return user == repo_obj.user.user or (
user in [usr.user for usr in repo_obj.users]
) or (user in usergrps)
def generate_user_key_files():
""" Regenerate the key files used by gitolite.
"""
gitolite_home = APP.config.get('GITOLITE_HOME', None)
if gitolite_home:
users = pagure.lib.search_user(SESSION)
for user in users:
pagure.lib.update_user_ssh(SESSION, user, user.public_ssh_key,
APP.config.get('GITOLITE_KEYDIR', None))
pagure.lib.git.generate_gitolite_acls()
def login_required(function):
""" Flask decorator to retrict access to logged in user.
If the auth system is ``fas`` it will also require that the user sign
the FPCA.
"""
auth_method = APP.config.get('PAGURE_AUTH', None)
@wraps(function)
def decorated_function(*args, **kwargs):
""" Decorated function, actually does the work. """
if flask.session.get('_justloggedout', False):
return flask.redirect(flask.url_for('.index'))
elif not authenticated():
return flask.redirect(
flask.url_for('auth_login', next=flask.request.url))
elif auth_method == 'fas' and not flask.g.fas_user.cla_done:
flask.flash('You must sign the FPCA (Fedora Project Contributor '
'Agreement) to use pagure', 'errors')
return flask.redirect(flask.url_for('.index'))
return function(*args, **kwargs)
return decorated_function
@APP.context_processor
def inject_variables():
""" With this decorator we can set some variables to all templates.
"""
user_admin = is_admin()
forkbuttonform = None
if authenticated():
forkbuttonform = pagure.forms.ConfirmationForm()
justlogedout = flask.session.get('_justloggedout', False)
if justlogedout:
flask.session['_justloggedout'] = None
def is_watching(reponame, username=None, namespace=None):
watch = False
if authenticated():
watch = pagure.lib.is_watching(
SESSION, flask.g.fas_user,
reponame,
repouser=username,
namespace=namespace)
return watch
return dict(
version=__version__,
admin=user_admin,
authenticated=authenticated(),
forkbuttonform=forkbuttonform,
is_watching=is_watching,
)
@APP.before_request
def set_session():
""" Set the flask session as permanent. """
flask.session.permanent = True
@APP.before_request
def set_variables():
""" This method retrieves the repo and username set in the URLs and
provides some of the variables that are most often used.
"""
# The API namespace has its own way of getting repo and username and
# of handling errors
if flask.request.blueprint == 'api_ns':
return
# Retrieve the variables in the URL
args = flask.request.view_args or {}
# Check if there is a `repo` and an `username`
repo = args.get('repo')
username = args.get('username')
namespace = args.get('namespace')
# If there isn't a `repo` in the URL path, or if there is but the
# endpoint called is part of the API, just don't do anything
if repo:
flask.g.repo = pagure.lib.get_project(
SESSION, repo, user=username, namespace=namespace)
if not flask.g.repo \
and APP.config.get('OLD_VIEW_COMMIT_ENABLED', False) \
and len(repo) == 40:
return flask.redirect(flask.url_for(
'view_commit', repo=namespace, commitid=repo,
username=username, namespace=None))
if flask.g.repo is None:
flask.abort(404, 'Project not found')
flask.g.reponame = get_repo_path(flask.g.repo)
flask.g.repo_obj = pygit2.Repository(flask.g.reponame)
flask.g.repo_admin = is_repo_admin(flask.g.repo)
flask.g.branches = sorted(flask.g.repo_obj.listall_branches())
items_per_page = 100
flask.g.offset = 0
flask.g.page = 1
flask.g.limit = items_per_page
page = flask.request.args.get('page')
limit = flask.request.args.get('n')
if limit:
try:
limit = int(limit)
except ValueError:
limit = 10
if limit > 500 or limit <= 0:
limit = items_per_page
flask.g.limit = limit
if page:
try:
page = abs(int(page))
except ValueError:
page = 1
if page <= 0:
page = 1
flask.g.page = page
flask.g.offset = (page - 1) * flask.g.limit
@APP.errorhandler(404)
def not_found(error):
"""404 Not Found page"""
return flask.render_template('not_found.html', error=error), 404
@APP.errorhandler(500)
def fatal_error(error): # pragma: no cover
"""500 Fatal Error page"""
return flask.render_template('fatal_error.html', error=error), 500
@APP.errorhandler(401)
def unauthorized(error): # pragma: no cover
"""401 Unauthorized page"""
return flask.render_template('unauthorized.html', error=error), 401
@APP.route('/login/', methods=('GET', 'POST'))
def auth_login(): # pragma: no cover
""" Method to log into the application using FAS OpenID. """
return_point = flask.url_for('index')
if 'next' in flask.request.args:
if is_safe_url(flask.request.args['next']):
return_point = flask.request.args['next']
if authenticated():
return flask.redirect(return_point)
admins = APP.config['ADMIN_GROUP']
if isinstance(admins, list):
admins = set(admins)
else: # pragma: no cover
admins = set([admins])
if APP.config.get('PAGURE_AUTH', None) in ['fas', 'openid']:
groups = set()
if not APP.config.get('ENABLE_GROUP_MNGT', False):
groups = [
group.group_name
for group in pagure.lib.search_groups(
SESSION, group_type='user')
]
groups = set(groups).union(admins)
return FAS.login(return_url=return_point, groups=groups)
elif APP.config.get('PAGURE_AUTH', None) == 'local':
form = pagure.login_forms.LoginForm()
return flask.render_template(
'login/login.html',
next_url=return_point,
form=form,
)
@APP.route('/logout/')
def auth_logout(): # pragma: no cover
""" Method to log out from the application. """
return_point = flask.url_for('index')
if 'next' in flask.request.args:
if is_safe_url(flask.request.args['next']):
return_point = flask.request.args['next']
if not authenticated():
return flask.redirect(return_point)
logout()
flask.flash("You have been logged out")
flask.session['_justloggedout'] = True
return flask.redirect(return_point)
def __get_file_in_tree(repo_obj, tree, filepath, bail_on_tree=False):
''' Retrieve the entry corresponding to the provided filename in a
given tree.
'''
filename = filepath[0]
if isinstance(tree, pygit2.Blob):
return
for entry in tree:
fname = entry.name.decode('utf-8')
if fname == filename:
if len(filepath) == 1:
blob = repo_obj.get(entry.id)
# If we can't get the content (for example: an empty folder)
if blob is None:
return
# If we get a tree instead of a blob, let's escape
if isinstance(blob, pygit2.Tree) and bail_on_tree:
return blob
content = blob.data
# If it's a (sane) symlink, we try a single-level dereference
if entry.filemode == pygit2.GIT_FILEMODE_LINK \
and os.path.normpath(content) == content \
and not os.path.isabs(content):
try:
dereferenced = tree[content]
except KeyError:
pass
else:
if dereferenced.filemode == pygit2.GIT_FILEMODE_BLOB:
blob = repo_obj[dereferenced.oid]
return blob
else:
nextitem = repo_obj[entry.oid]
# If we can't get the content (for example: an empty folder)
if nextitem is None:
return
return __get_file_in_tree(
repo_obj, nextitem, filepath[1:],
bail_on_tree=bail_on_tree)
def get_repo_path(repo):
""" Return the path of the git repository corresponding to the provided
Repository object from the DB.
"""
repopath = os.path.join(APP.config['GIT_FOLDER'], repo.path)
if not os.path.exists(repopath):
flask.abort(404, 'No git repo found')
return repopath
def get_remote_repo_path(remote_git, branch_from, loop=False):
""" Return the path of the remote git repository corresponding to the
provided information.
"""
repopath = os.path.join(
APP.config['REMOTE_GIT_FOLDER'],
werkzeug.secure_filename('%s_%s' % (remote_git, branch_from))
)
if not os.path.exists(repopath):
try:
pygit2.clone_repository(
remote_git, repopath, checkout_branch=branch_from)
except Exception as err:
LOG.debug(err)
LOG.exception(err)
flask.abort(500, 'Could not clone the remote git repository')
else:
repo = pagure.lib.repo.PagureRepo(repopath)
try:
repo.pull(branch=branch_from, force=True)
except pagure.exceptions.PagureException as err:
LOG.debug(err)
LOG.exception(err)
flask.abort(500, err.message)
return repopath
# Import the application
import pagure.ui.app
import pagure.ui.admin
import pagure.ui.fork
import pagure.ui.groups
if APP.config.get('ENABLE_TICKETS', True):
import pagure.ui.issues
import pagure.ui.plugins
import pagure.ui.repo
from pagure.api import API
APP.register_blueprint(API)
import pagure.internal
APP.register_blueprint(pagure.internal.PV)
# Only import the login controller if the app is set up for local login
if APP.config.get('PAGURE_AUTH', None) == 'local':
import pagure.ui.login as login
APP.before_request_funcs[None].insert(0, login._check_session_cookie)
APP.after_request(login._send_session_cookie)
# pylint: disable=unused-argument
@APP.teardown_request
def shutdown_session(exception=None):
""" Remove the DB session at the end of each request. """
SESSION.remove()

Some files were not shown because too many files have changed in this diff Show more