code:
# -*- 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'


code:
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 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 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``

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 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 to configure the SMTP server to use when
sending emails.

Defaults to: ``localhost``.

SMTP_PORT
~~~~~~~~~

This configuration key allow 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 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 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 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 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 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 to specify which IP addresses are allowed
to access the internal API endpoint. These endpoints are accessed by the
milters for example and allow 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 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 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 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 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``.

repo_name: bill/auger

license: mit


code:
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>`_.


code:
Contributors to pagure
=========================

Pagure would be nothing without its contributors.

On July 15, 2016 (release 2.3.3), the list looks as follow:

=================  ===========
Number of commits  Contributor
=================  ===========
  4105              Pierre-Yves Chibon <pingou@pingoured.fr>
   174              Ryan Lerch <rlerch@redhat.com>
    61              farhaanbukhsh <farhaan.bukhsh@gmail.com>
    59              Johan Cwiklinski <johan@x-tnd.be>
    48              Clement Verna <cverna@tutanota.com>
    36              Vivek Anand <vivekanand1101@gmail.com>
    18              Sayan Chowdhury <sayan.chowdhury2012@gmail.com>
    15              Gaurav Kumar <aavrug@gmail.com>
    15              Lubomír Sedlář <lsedlar@redhat.com>
    15              Patrick Uiterwijk <puiterwijk@redhat.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              vanzhiganov <vanzhiganov@ya.ru>
     5              yangl1996 <yltt1234512@gmail.com>
     4              Maciej Lasyk <maciek@lasyk.info>
     4              Paul W. Frields <stickster@gmail.com>
     3              Ankush Behl <cloudbehl@gmail.com>
     3              Anthony Lackey <alackey96@gmail.com>
     3              Dhriti Shikhar <dhriti.shikhar.rokz@gmail.com>
     3              Eric Barbour <ebarbour@redhat.com>
     3              Jan Pokorný <jpokorny@redhat.com>
     3              Kushal Khandelwal <kushal124@gmail.com>
     3              Pedro Lima <pedro.lima@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              bruno <bruno@wolff.to>
     2              dhrish20 <dhrish20@gmail.com>
     1              Anthony Lackey <alackey@localhost.localdomain>
     1              David Caro <dcaroest@redhat.com>
     1              Eric Barbour <emb4gu@virginia.edu>
     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              Vyacheslav Anzhiganov <vanzhiganov@ya.ru>
     1              Yves Martin <ymartin1040@gmail.com>
     1              abhishek <abhishekarora12@gmail.com>
     1              jcvicelli <jcvicelli@gmail.com>
     1              pingou <pingou@fedoraproject.org>
     1              ryanlerch <rlerch@redhat.com>
     1              skrzepto <shims506@gmail.com>
     1              skrzepto <skrzepto@gmail.com>
     1              tenstormavi <avi.avinash3008@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.


code:
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 immediatly.

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


code:
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
   configuration
   development
   usage
   contributing
   contributors

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`


code:
Installing pagure
=================

There are two ways to install pagure:

* via the RPM package (recommanded 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.


code:
Installing pagure's EventSource server
======================================

Eventsource or Server Sent Events are messages sent from a server to a web
browser. It allows 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


code:
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


code:
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 occured.

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


code:
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``.


code:
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.


code:
=========
Pagure CI
=========

Pagure CI is a continuous integration tool using which the PR on the projects
can be tested and flaged with the status of the build.

How to enable Pagure CI
=======================

* Enable the Fedmsg plugin in pagure project setting . This will emit the message
  to for consumer to consume it.

* Fill in the Pagure CI form with the required details.

::

		Pagure Project Name
		Jenkins Project Name
		Jenkins Token
		Jenkins Url

        All of which are required field.

* The jenkins token is any string that you give here. The only thing that should
  be kept in mind that this token should be same through out.

* This will give a POST URL which will be used for Job Notification in Jenkins

* The POST url will only appear only after you successfully submitted the form.

Configuring Jenkins
===================

Jenkins configuration is the most important part of how the Pagure CI works,
after you login to your Jenkins Instance.

* Go to Manage Jenkins -> Configuire Global Security and under that select
  `Project-based Matrix Authorization Strategy`

* Add your username here and make sure to give that username all the permissions.
  You should give all the permissions possible so that you save your self from
  getting locked in Jenkins.

* Download the following plugins:

::

      Build Authorization Root Plugin
      Git Plugins
      Notification Plugin

* Click on the New Item

* Select Freestyle Project

* Click OK and enter the name of the project, make sure the project name
  you filled in the Pagure CI form should match the name you entered here.

* Under 'Job Notification'  click 'Add Endpoint'

* Fields in Endpoint will be :

::

		FORMAT: JSON
		PROTOCOL: HTTP
		EVENT: Job Finalized
		URL: <The POST URL that Jenkins form returned>
		TIMEOUT: 3000
		LOG: 1

* Tick the build is parameterized

* From the Add Parameter drop down select String Parameter

* Two string parameters need to be created 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 give the same token
  that you gave in the Pagure CI form.

* Under Build -> Add build step -> Execute Shell

* In the box given  enter the shell steps you want for testing your project.

Example Script

::

        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

How to install Pagure CI
========================

Pagure CI requires `fedmsg` to run since it uses a consumer to get messages
and take appropriate actions. The dependency that is required is `fedmdg-hubs`.
For that the steps are given.

To install the dependencies required:

	 `dnf install fedmsg-hub`

`fedmsg` apart from the consumer require a file that tells to which cosumer
it should listen to. This file basically enable the consumer in PagureCI/.
For doing that, we need to place this file in appropriate directory.

	 `sudo cp pagure/fedmsg.d/pagure_ci.py /etc/fedmsg.d/`

Since the deployment is done using rpm, the next step is covered using  `setup.py`
which binds the consumer with the environment, this is done while building the rpm
so if rpm is already built this is not explicitly required.

	`python setup.py install`

Run the service:

    `sudo systemctl enable fedmsg-hub.service`

    `sudo systemctl start fedmsg-hub.service`


code:
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


code:
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, login 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

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!


code:
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.


code:
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.


code:
Using the roadmap feature
=========================

Pagure allows building the roadmap of the project using the tickets and
their tags.

The principal is as follow:

* All the ticket with the tag ``roadmap`` will show up on the roadmap page.
* For each milestones defined in the settings of the project, the roadmap
will group tickets with the corresponding tag.
* Tickets with the tag ``roadmap`` that are not associated with any of the
milestones defined in the settings are group in an ``unplanned`` section.

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 add them the tags: ``roadmap`` in combination with the milestone you want
``v1.0`` or ``v2.0``, or none of them if the ticket is on the roadmap but
not assigned to any milestones.

* And this is how it will look like

.. image:: _static/pagure_roadmap2.png
        :target: _static/pagure_roadmap2.png


code:
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 <https://pagure.io/flask-multistatic/>`_.


code:
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 formated 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.


code:
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.


code:
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/docs/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


code:
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 occured. 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>`_


code:
#!/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()


code:
[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


code:
""" This is an example pagure configuration for fedmsg.
By convention, it is normally installed as ``/etc/fedmsg.d/pagure.py``

For Fedora Infrastructure, our own version of this file is kept in
``ansible/roles/fedmsg/base/templates/``

It needs to be globally available so remote consumers know how to find the
pagure producer (wsgi process).
"""

import socket
hostname = socket.gethostname().split('.')[0]

config = dict(
    endpoints={
        "pagure.%s" % hostname: [
            "tcp://127.0.0.1:3005",
        ],
    },
)


code:
config = {
    'integrator.enabled': True,
}


code:
# 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


code:
#!/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)


code:
#-*- 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


code:
#!/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)