From 6f6808c9d3da7df3a3243b3707e15af2e48acceb Mon Sep 17 00:00:00 2001 From: Sergio Durigan Junior Date: Sun, 7 Aug 2016 23:59:31 -0400 Subject: [PATCH] Imported Upstream version 2.3.4 --- LICENSE | 340 + MANIFEST.in | 10 + PKG-INFO | 11 + README.rst | 81 + UPGRADING.rst | 187 + alembic/env.py | 71 + alembic/script.py.mako | 22 + .../15ea3c2cf83d_pr_comment_editing.py | 45 + .../1b6d7dc5600a_versioning_passwords.py | 38 + .../1cd0a853c697_add_closed_at_field_in_pr.py | 37 + ...1a_add_the_tree_id_column_to_pr_inline_.py | 29 + ...db0a833d35_add_notifications_to_tickets.py | 33 + .../257a7ce22682_add_the_remote_git_entry.py | 46 + ...3039_change_the_status_of_pull_requests.py | 60 + .../2aa7b3958bc5_add_the_milestones_column.py | 31 + alembic/versions/317a285e04a8_delete_hooks.py | 59 + ...116bb7a69b_add_the_url_field_to_project.py | 29 + .../3b441ef4e928_comment_editing_issue.py | 44 + ...14b855b_add_an_avatar_email_for_project.py | 29 + ...88a87_add_closed_at_attribute_in_issues.py | 27 + ...8_up_to_255_characters_for_project_name.py | 32 + .../versions/496f7a700f2e_add_priorities.py | 37 + ...add_the_initial_comment_on_the_pr_table.py | 29 + ...8e60d869326_add_notification_bool_to_pr.py | 33 + ...bed0_add_the_updated_on_column_to_pull_.py | 43 + ...d60fa_add_the_closed_by_column_to_pull_.py | 34 + ..._add_merge_status_to_the_pull_requests_.py | 35 + createdb.py | 14 + doc/Makefile | 153 + doc/_static/overview.png | Bin 0 -> 38621 bytes doc/_static/overview_simple.png | Bin 0 -> 18776 bytes doc/_static/pagure.png | Bin 0 -> 4336 bytes doc/_static/site.css | 16 + doc/_templates/pagure-logo.html | 1 + doc/api.rst | 5 + doc/conf.py | 317 + doc/configuration.rst | 508 + doc/contributing.rst | 27 + doc/contributors.rst | 75 + doc/development.rst | 261 + doc/index.rst | 51 + doc/install.rst | 264 + doc/install_evs.rst | 48 + doc/install_milter.rst | 79 + doc/install_webhooks.rst | 49 + doc/milter.rst | 62 + doc/overview.ascii | 53 + doc/overview.rst | 104 + doc/overview_simple.ascii | 26 + doc/requirements.txt | 2 + doc/usage.rst | 43 + doc/usage/_static/pagure_custom_pr.png | Bin 0 -> 67825 bytes doc/usage/_static/pagure_my_settings.png | Bin 0 -> 20806 bytes doc/usage/_static/pagure_roadmap2.png | Bin 0 -> 64198 bytes doc/usage/_static/pagure_ticket_template.png | Bin 0 -> 50123 bytes doc/usage/first_steps.rst | 80 + doc/usage/pr_custom_page.rst | 74 + doc/usage/project_settings.rst | 145 + doc/usage/roadmap.rst | 35 + doc/usage/theming.rst | 81 + doc/usage/ticket_templates.rst | 77 + doc/usage/upgrade_db.rst | 48 + doc/usage/using_doc.rst | 108 + doc/usage/using_webhooks.rst | 57 + ev-server/pagure-stream-server.py | 248 + ev-server/pagure_ev.service | 14 + files/alembic.ini | 60 + files/api_key_expire_mail.py | 55 + files/doc_pagure.wsgi | 23 + files/emoji_clean_json.py | 32 + files/gitolite.rc | 233 + files/gitolite3.rc | 195 + files/load_from_disk.py | 118 + files/pagure.cfg.sample | 214 + files/pagure.conf | 114 + files/pagure.spec | 1083 ++ files/pagure.wsgi | 28 + milters/comment_email_milter.py | 247 + milters/milter_tempfile.conf | 1 + milters/pagure_milter.service | 14 + pagure.egg-info/PKG-INFO | 11 + pagure.egg-info/SOURCES.txt | 340 + pagure.egg-info/dependency_links.txt | 1 + pagure.egg-info/requires.txt | 31 + pagure.egg-info/top_level.txt | 1 + pagure/__init__.py | 556 + pagure/api/__init__.py | 496 + pagure/api/fork.py | 650 ++ pagure/api/issue.py | 729 ++ pagure/api/project.py | 346 + pagure/api/user.py | 110 + pagure/default_config.py | 219 + pagure/doc/api.rst | 69 + pagure/doc_utils.py | 122 + pagure/docs_server.py | 183 + pagure/exceptions.py | 59 + pagure/forms.py | 423 + pagure/hooks/__init__.py | 123 + pagure/hooks/fedmsg.py | 90 + pagure/hooks/files/fedmsg_hook.py | 91 + pagure/hooks/files/git_multimail.py | 2589 +++++ pagure/hooks/files/pagure_block_unsigned.py | 73 + .../hooks/files/pagure_force_commit_hook.py | 82 + pagure/hooks/files/pagure_hook.py | 195 + pagure/hooks/files/pagure_hook_requests.py | 102 + pagure/hooks/files/pagure_hook_tickets.py | 97 + pagure/hooks/files/post-receive | 23 + pagure/hooks/files/pre-receive | 23 + pagure/hooks/files/rtd_hook.py | 81 + pagure/hooks/irc.py | 138 + pagure/hooks/mail.py | 108 + pagure/hooks/pagure_force_commit.py | 103 + pagure/hooks/pagure_hook.py | 138 + pagure/hooks/pagure_request_hook.py | 119 + pagure/hooks/pagure_ticket_hook.py | 118 + pagure/hooks/pagure_unsigned_commits.py | 96 + pagure/hooks/rtd.py | 104 + pagure/internal/__init__.py | 503 + pagure/lib/__init__.py | 2875 +++++ pagure/lib/git.py | 1284 +++ pagure/lib/link.py | 84 + pagure/lib/login.py | 101 + pagure/lib/model.py | 1528 +++ pagure/lib/notify.py | 546 + pagure/lib/repo.py | 78 + pagure/login_forms.py | 103 + pagure/mail_logging.py | 195 + pagure/pfmarkdown.py | 235 + pagure/proxy.py | 71 + pagure/static/atwho/jquery.atwho.min.css | 1 + pagure/static/atwho/jquery.atwho.min.js | 1 + pagure/static/atwho/jquery.caret.min.js | 2 + pagure/static/codemirror/codemirror.css | 334 + pagure/static/codemirror/codemirror.js | 8872 +++++++++++++++ pagure/static/codemirror/solarized.css | 163 + pagure/static/emoji/emoji_strategy.json | 1 + pagure/static/emoji/emojione.min.js | 63 + pagure/static/emoji/emojione.sprites.css | 72 + pagure/static/emoji/emojione.sprites.png | Bin 0 -> 2466435 bytes .../static/emoji/jquery.textcomplete.min.js | 4 + pagure/static/favicon.ico | Bin 0 -> 2462 bytes pagure/static/fonts/fonts.css | 84 + .../open-sans-v13-latin_latin-ext-300.eot | Bin 0 -> 31896 bytes .../open-sans-v13-latin_latin-ext-300.svg | 5074 +++++++++ .../open-sans-v13-latin_latin-ext-300.ttf | Bin 0 -> 68444 bytes .../open-sans-v13-latin_latin-ext-300.woff | Bin 0 -> 33704 bytes .../open-sans-v13-latin_latin-ext-300.woff2 | Bin 0 -> 23816 bytes ...pen-sans-v13-latin_latin-ext-300italic.eot | Bin 0 -> 30763 bytes ...pen-sans-v13-latin_latin-ext-300italic.svg | 5087 +++++++++ ...pen-sans-v13-latin_latin-ext-300italic.ttf | Bin 0 -> 64668 bytes ...en-sans-v13-latin_latin-ext-300italic.woff | Bin 0 -> 32300 bytes ...n-sans-v13-latin_latin-ext-300italic.woff2 | Bin 0 -> 22220 bytes .../open-sans-v13-latin_latin-ext-700.eot | Bin 0 -> 32088 bytes .../open-sans-v13-latin_latin-ext-700.svg | 5076 +++++++++ .../open-sans-v13-latin_latin-ext-700.ttf | Bin 0 -> 68776 bytes .../open-sans-v13-latin_latin-ext-700.woff | Bin 0 -> 33876 bytes .../open-sans-v13-latin_latin-ext-700.woff2 | Bin 0 -> 23652 bytes ...pen-sans-v13-latin_latin-ext-700italic.eot | Bin 0 -> 30546 bytes ...pen-sans-v13-latin_latin-ext-700italic.svg | 5084 +++++++++ ...pen-sans-v13-latin_latin-ext-700italic.ttf | Bin 0 -> 65236 bytes ...en-sans-v13-latin_latin-ext-700italic.woff | Bin 0 -> 32180 bytes ...n-sans-v13-latin_latin-ext-700italic.woff2 | Bin 0 -> 22176 bytes .../open-sans-v13-latin_latin-ext-italic.eot | Bin 0 -> 30880 bytes .../open-sans-v13-latin_latin-ext-italic.svg | 5091 +++++++++ .../open-sans-v13-latin_latin-ext-italic.ttf | Bin 0 -> 65072 bytes .../open-sans-v13-latin_latin-ext-italic.woff | Bin 0 -> 32212 bytes ...open-sans-v13-latin_latin-ext-italic.woff2 | Bin 0 -> 22196 bytes .../open-sans-v13-latin_latin-ext-regular.eot | Bin 0 -> 31491 bytes .../open-sans-v13-latin_latin-ext-regular.svg | 5078 +++++++++ .../open-sans-v13-latin_latin-ext-regular.ttf | Bin 0 -> 66740 bytes ...open-sans-v13-latin_latin-ext-regular.woff | Bin 0 -> 33060 bytes ...pen-sans-v13-latin_latin-ext-regular.woff2 | Bin 0 -> 23048 bytes .../hack_fonts/css/hack-extended.min.css | 4 + .../fonts/eot/hack-bold-webfont.eot | Bin 0 -> 138148 bytes .../fonts/eot/hack-bolditalic-webfont.eot | Bin 0 -> 145831 bytes .../fonts/eot/hack-italic-webfont.eot | Bin 0 -> 142504 bytes .../fonts/eot/hack-regular-webfont.eot | Bin 0 -> 136709 bytes .../eot/latin/hack-bold-latin-webfont.eot | Bin 0 -> 29259 bytes .../latin/hack-bolditalic-latin-webfont.eot | Bin 0 -> 31810 bytes .../eot/latin/hack-italic-latin-webfont.eot | Bin 0 -> 30946 bytes .../eot/latin/hack-regular-latin-webfont.eot | Bin 0 -> 28872 bytes .../fonts/svg/hack-bold-webfont.svg | 1645 +++ .../fonts/svg/hack-bolditalic-webfont.svg | 1591 +++ .../fonts/svg/hack-italic-webfont.svg | 1588 +++ .../fonts/svg/hack-regular-webfont.svg | 1570 +++ .../svg/latin/hack-bold-latin-webfont.svg | 241 + .../latin/hack-bolditalic-latin-webfont.svg | 241 + .../svg/latin/hack-italic-latin-webfont.svg | 241 + .../svg/latin/hack-regular-latin-webfont.svg | 241 + .../fonts/web-ttf/hack-bold-webfont.ttf | Bin 0 -> 383108 bytes .../fonts/web-ttf/hack-bolditalic-webfont.ttf | Bin 0 -> 391984 bytes .../fonts/web-ttf/hack-italic-webfont.ttf | Bin 0 -> 386972 bytes .../fonts/web-ttf/hack-regular-webfont.ttf | Bin 0 -> 381628 bytes .../web-ttf/latin/hack-bold-latin-webfont.ttf | Bin 0 -> 73836 bytes .../latin/hack-bolditalic-latin-webfont.ttf | Bin 0 -> 78712 bytes .../latin/hack-italic-latin-webfont.ttf | Bin 0 -> 77176 bytes .../latin/hack-regular-latin-webfont.ttf | Bin 0 -> 73896 bytes .../fonts/woff/hack-bold-webfont.woff | Bin 0 -> 170476 bytes .../fonts/woff/hack-bolditalic-webfont.woff | Bin 0 -> 178776 bytes .../fonts/woff/hack-italic-webfont.woff | Bin 0 -> 175388 bytes .../fonts/woff/hack-regular-webfont.woff | Bin 0 -> 167436 bytes .../woff/latin/hack-bold-latin-webfont.woff | Bin 0 -> 32512 bytes .../latin/hack-bolditalic-latin-webfont.woff | Bin 0 -> 35176 bytes .../woff/latin/hack-italic-latin-webfont.woff | Bin 0 -> 34072 bytes .../latin/hack-regular-latin-webfont.woff | Bin 0 -> 31960 bytes .../fonts/woff2/hack-bold-webfont.woff2 | Bin 0 -> 123708 bytes .../fonts/woff2/hack-bolditalic-webfont.woff2 | Bin 0 -> 130804 bytes .../fonts/woff2/hack-italic-webfont.woff2 | Bin 0 -> 127696 bytes .../fonts/woff2/hack-regular-webfont.woff2 | Bin 0 -> 122272 bytes .../woff2/latin/hack-bold-latin-webfont.woff2 | Bin 0 -> 26440 bytes .../latin/hack-bolditalic-latin-webfont.woff2 | Bin 0 -> 28788 bytes .../latin/hack-italic-latin-webfont.woff2 | Bin 0 -> 28056 bytes .../latin/hack-regular-latin-webfont.woff2 | Bin 0 -> 26020 bytes pagure/static/images/link.png | Bin 0 -> 514 bytes pagure/static/images/spinner.gif | Bin 0 -> 4077 bytes pagure/static/images/users.png | Bin 0 -> 315 bytes pagure/static/issue_ev.js | 311 + pagure/static/jquery-1.10.2.js | 9789 +++++++++++++++++ pagure/static/jquery-ui-1.11.2.custom.min.js | 33 + pagure/static/jquery.dotdotdot.min.js | 13 + .../open_iconic_1.1.0/css/open-iconic.min.css | 1 + .../open_iconic_1.1.0/fonts/open-iconic.eot | Bin 0 -> 28196 bytes .../open_iconic_1.1.0/fonts/open-iconic.otf | Bin 0 -> 20996 bytes .../open_iconic_1.1.0/fonts/open-iconic.svg | 543 + .../open_iconic_1.1.0/fonts/open-iconic.ttf | Bin 0 -> 28028 bytes .../open_iconic_1.1.0/fonts/open-iconic.woff | Bin 0 -> 14984 bytes pagure/static/pagure-logo.png | Bin 0 -> 6917 bytes pagure/static/pagure.css | 566 + pagure/static/request_ev.js | 157 + pagure/static/selectize.bootstrap3.css | 401 + pagure/static/selectize.min.js | 3 + pagure/static/stupidtable.min.js | 4 + pagure/static/toggle.css | 139 + pagure/static/upload.js | 152 + pagure/templates/_browseheader.html | 40 + pagure/templates/_formhelper.html | 206 + pagure/templates/_render_repo.html | 264 + pagure/templates/activity.html | 270 + pagure/templates/add_group.html | 30 + pagure/templates/add_group_project.html | 70 + pagure/templates/add_token.html | 40 + pagure/templates/add_user.html | 68 + pagure/templates/admin_index.html | 50 + pagure/templates/api.html | 138 + pagure/templates/comment_update.html | 35 + pagure/templates/commit.html | 187 + pagure/templates/commits.html | 178 + pagure/templates/doc_ssh_keys.html | 40 + pagure/templates/docs.html | 20 + pagure/templates/edit_file.html | 122 + pagure/templates/edit_tag.html | 28 + pagure/templates/fatal_error.html | 16 + pagure/templates/file.html | 222 + pagure/templates/forks.html | 43 + pagure/templates/group_info.html | 185 + pagure/templates/group_list.html | 157 + pagure/templates/index.html | 102 + pagure/templates/index_auth.html | 207 + pagure/templates/issue.html | 696 ++ pagure/templates/issues.html | 219 + pagure/templates/login/login.html | 42 + pagure/templates/login/password_change.html | 29 + pagure/templates/login/password_recover.html | 33 + pagure/templates/login/password_reset.html | 29 + pagure/templates/login/user_new.html | 36 + pagure/templates/master.html | 162 + pagure/templates/new_issue.html | 225 + pagure/templates/new_project.html | 32 + pagure/templates/new_release.html | 33 + pagure/templates/not_found.html | 25 + pagure/templates/plugin.html | 32 + pagure/templates/pull_request.html | 1272 +++ pagure/templates/pull_request_comment.html | 36 + pagure/templates/pull_request_title.html | 50 + pagure/templates/releases.html | 65 + pagure/templates/remote_pull_request.html | 59 + pagure/templates/repo_info.html | 322 + pagure/templates/repo_master.html | 269 + pagure/templates/requests.html | 234 + pagure/templates/roadmap.html | 168 + pagure/templates/settings.html | 671 ++ pagure/templates/unauthorized.html | 18 + pagure/templates/user_emails.html | 26 + pagure/templates/user_info.html | 123 + pagure/templates/user_list.html | 159 + pagure/templates/user_requests.html | 109 + pagure/templates/user_settings.html | 123 + pagure/ui/__init__.py | 9 + pagure/ui/admin.py | 96 + pagure/ui/app.py | 667 ++ pagure/ui/filters.py | 419 + pagure/ui/fork.py | 1163 ++ pagure/ui/groups.py | 262 + pagure/ui/issues.py | 1044 ++ pagure/ui/login.py | 466 + pagure/ui/plugins.py | 157 + pagure/ui/repo.py | 1951 ++++ requirements.txt | 38 + setup.cfg | 5 + setup.py | 58 + tests/__init__.py | 546 + tests/placebo.png | Bin 0 -> 2557 bytes tests/test_config | 1 + tests/test_pagure_flask_api.py | 165 + tests/test_pagure_flask_api_auth.py | 173 + tests/test_pagure_flask_api_fork.py | 786 ++ tests/test_pagure_flask_api_issue.py | 1344 +++ tests/test_pagure_flask_api_project.py | 433 + tests/test_pagure_flask_docs.py | 200 + tests/test_pagure_flask_dump_load_ticket.py | 236 + tests/test_pagure_flask_internal.py | 1003 ++ tests/test_pagure_flask_ui_admin.py | 257 + tests/test_pagure_flask_ui_app.py | 879 ++ tests/test_pagure_flask_ui_fork.py | 1847 ++++ tests/test_pagure_flask_ui_groups.py | 413 + tests/test_pagure_flask_ui_issues.py | 1728 +++ tests/test_pagure_flask_ui_login.py | 592 + .../test_pagure_flask_ui_no_master_branch.py | 346 + tests/test_pagure_flask_ui_plugins.py | 157 + tests/test_pagure_flask_ui_plugins_fedmsg.py | 174 + tests/test_pagure_flask_ui_plugins_irc.py | 178 + tests/test_pagure_flask_ui_plugins_mail.py | 209 + tests/test_pagure_flask_ui_plugins_noff.py | 237 + ...est_pagure_flask_ui_plugins_pagure_hook.py | 179 + ...re_flask_ui_plugins_pagure_request_hook.py | 193 + ...ure_flask_ui_plugins_pagure_ticket_hook.py | 193 + .../test_pagure_flask_ui_plugins_rtd_hook.py | 195 + .../test_pagure_flask_ui_plugins_unsigned.py | 183 + tests/test_pagure_flask_ui_priorities.py | 354 + tests/test_pagure_flask_ui_repo.py | 3171 ++++++ tests/test_pagure_flask_ui_repo_slash_name.py | 258 + tests/test_pagure_flask_ui_roadmap.py | 515 + .../test_pagure_flask_ui_slash_branch_name.py | 383 + tests/test_pagure_lib.py | 2371 ++++ tests/test_pagure_lib_git.py | 1383 +++ tests/test_pagure_lib_git_get_tags_objects.py | 110 + tests/test_pagure_lib_link.py | 257 + tests/test_pagure_lib_login.py | 113 + tests/test_pagure_lib_model.py | 148 + tests/test_zzz_pagure_flask_ui_old_commit.py | 251 + webhook-server/pagure-webhook-server.py | 161 + webhook-server/pagure_webhook.service | 14 + 342 files changed, 119703 insertions(+) create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 PKG-INFO create mode 100644 README.rst create mode 100644 UPGRADING.rst create mode 100644 alembic/env.py create mode 100644 alembic/script.py.mako create mode 100644 alembic/versions/15ea3c2cf83d_pr_comment_editing.py create mode 100644 alembic/versions/1b6d7dc5600a_versioning_passwords.py create mode 100644 alembic/versions/1cd0a853c697_add_closed_at_field_in_pr.py create mode 100644 alembic/versions/1f3de3853a1a_add_the_tree_id_column_to_pr_inline_.py create mode 100644 alembic/versions/22db0a833d35_add_notifications_to_tickets.py create mode 100644 alembic/versions/257a7ce22682_add_the_remote_git_entry.py create mode 100644 alembic/versions/298891e63039_change_the_status_of_pull_requests.py create mode 100644 alembic/versions/2aa7b3958bc5_add_the_milestones_column.py create mode 100644 alembic/versions/317a285e04a8_delete_hooks.py create mode 100644 alembic/versions/36116bb7a69b_add_the_url_field_to_project.py create mode 100644 alembic/versions/3b441ef4e928_comment_editing_issue.py create mode 100644 alembic/versions/3c25e14b855b_add_an_avatar_email_for_project.py create mode 100644 alembic/versions/43df5e588a87_add_closed_at_attribute_in_issues.py create mode 100644 alembic/versions/443e090da188_up_to_255_characters_for_project_name.py create mode 100644 alembic/versions/496f7a700f2e_add_priorities.py create mode 100644 alembic/versions/4cae55a80a42_add_the_initial_comment_on_the_pr_table.py create mode 100644 alembic/versions/58e60d869326_add_notification_bool_to_pr.py create mode 100644 alembic/versions/6190226bed0_add_the_updated_on_column_to_pull_.py create mode 100644 alembic/versions/abc71fd60fa_add_the_closed_by_column_to_pull_.py create mode 100644 alembic/versions/b5efae6bb23_add_merge_status_to_the_pull_requests_.py create mode 100644 createdb.py create mode 100644 doc/Makefile create mode 100644 doc/_static/overview.png create mode 100644 doc/_static/overview_simple.png create mode 100644 doc/_static/pagure.png create mode 100644 doc/_static/site.css create mode 100644 doc/_templates/pagure-logo.html create mode 100644 doc/api.rst create mode 100644 doc/conf.py create mode 100644 doc/configuration.rst create mode 100644 doc/contributing.rst create mode 100644 doc/contributors.rst create mode 100644 doc/development.rst create mode 100644 doc/index.rst create mode 100644 doc/install.rst create mode 100644 doc/install_evs.rst create mode 100644 doc/install_milter.rst create mode 100644 doc/install_webhooks.rst create mode 100644 doc/milter.rst create mode 100644 doc/overview.ascii create mode 100644 doc/overview.rst create mode 100644 doc/overview_simple.ascii create mode 100644 doc/requirements.txt create mode 100644 doc/usage.rst create mode 100644 doc/usage/_static/pagure_custom_pr.png create mode 100644 doc/usage/_static/pagure_my_settings.png create mode 100644 doc/usage/_static/pagure_roadmap2.png create mode 100644 doc/usage/_static/pagure_ticket_template.png create mode 100644 doc/usage/first_steps.rst create mode 100644 doc/usage/pr_custom_page.rst create mode 100644 doc/usage/project_settings.rst create mode 100644 doc/usage/roadmap.rst create mode 100644 doc/usage/theming.rst create mode 100644 doc/usage/ticket_templates.rst create mode 100644 doc/usage/upgrade_db.rst create mode 100644 doc/usage/using_doc.rst create mode 100644 doc/usage/using_webhooks.rst create mode 100644 ev-server/pagure-stream-server.py create mode 100644 ev-server/pagure_ev.service create mode 100644 files/alembic.ini create mode 100644 files/api_key_expire_mail.py create mode 100644 files/doc_pagure.wsgi create mode 100644 files/emoji_clean_json.py create mode 100755 files/gitolite.rc create mode 100644 files/gitolite3.rc create mode 100644 files/load_from_disk.py create mode 100644 files/pagure.cfg.sample create mode 100644 files/pagure.conf create mode 100644 files/pagure.spec create mode 100644 files/pagure.wsgi create mode 100644 milters/comment_email_milter.py create mode 100644 milters/milter_tempfile.conf create mode 100644 milters/pagure_milter.service create mode 100644 pagure.egg-info/PKG-INFO create mode 100644 pagure.egg-info/SOURCES.txt create mode 100644 pagure.egg-info/dependency_links.txt create mode 100644 pagure.egg-info/requires.txt create mode 100644 pagure.egg-info/top_level.txt create mode 100644 pagure/__init__.py create mode 100644 pagure/api/__init__.py create mode 100644 pagure/api/fork.py create mode 100644 pagure/api/issue.py create mode 100644 pagure/api/project.py create mode 100644 pagure/api/user.py create mode 100644 pagure/default_config.py create mode 100644 pagure/doc/api.rst create mode 100644 pagure/doc_utils.py create mode 100644 pagure/docs_server.py create mode 100644 pagure/exceptions.py create mode 100644 pagure/forms.py create mode 100644 pagure/hooks/__init__.py create mode 100644 pagure/hooks/fedmsg.py create mode 100755 pagure/hooks/files/fedmsg_hook.py create mode 100755 pagure/hooks/files/git_multimail.py create mode 100755 pagure/hooks/files/pagure_block_unsigned.py create mode 100755 pagure/hooks/files/pagure_force_commit_hook.py create mode 100755 pagure/hooks/files/pagure_hook.py create mode 100755 pagure/hooks/files/pagure_hook_requests.py create mode 100755 pagure/hooks/files/pagure_hook_tickets.py create mode 100755 pagure/hooks/files/post-receive create mode 100755 pagure/hooks/files/pre-receive create mode 100755 pagure/hooks/files/rtd_hook.py create mode 100644 pagure/hooks/irc.py create mode 100644 pagure/hooks/mail.py create mode 100644 pagure/hooks/pagure_force_commit.py create mode 100644 pagure/hooks/pagure_hook.py create mode 100644 pagure/hooks/pagure_request_hook.py create mode 100644 pagure/hooks/pagure_ticket_hook.py create mode 100644 pagure/hooks/pagure_unsigned_commits.py create mode 100644 pagure/hooks/rtd.py create mode 100644 pagure/internal/__init__.py create mode 100644 pagure/lib/__init__.py create mode 100644 pagure/lib/git.py create mode 100644 pagure/lib/link.py create mode 100644 pagure/lib/login.py create mode 100644 pagure/lib/model.py create mode 100644 pagure/lib/notify.py create mode 100644 pagure/lib/repo.py create mode 100644 pagure/login_forms.py create mode 100644 pagure/mail_logging.py create mode 100644 pagure/pfmarkdown.py create mode 100644 pagure/proxy.py create mode 100644 pagure/static/atwho/jquery.atwho.min.css create mode 100644 pagure/static/atwho/jquery.atwho.min.js create mode 100644 pagure/static/atwho/jquery.caret.min.js create mode 100644 pagure/static/codemirror/codemirror.css create mode 100644 pagure/static/codemirror/codemirror.js create mode 100644 pagure/static/codemirror/solarized.css create mode 100644 pagure/static/emoji/emoji_strategy.json create mode 100644 pagure/static/emoji/emojione.min.js create mode 100755 pagure/static/emoji/emojione.sprites.css create mode 100644 pagure/static/emoji/emojione.sprites.png create mode 100644 pagure/static/emoji/jquery.textcomplete.min.js create mode 100644 pagure/static/favicon.ico create mode 100644 pagure/static/fonts/fonts.css create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300.eot create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300.svg create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300.ttf create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300.woff create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300.woff2 create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.eot create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.svg create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.ttf create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.woff create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-300italic.woff2 create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700.eot create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700.svg create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700.ttf create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700.woff create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700.woff2 create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.eot create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.svg create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.ttf create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.woff create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-700italic.woff2 create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.eot create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.svg create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.ttf create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.woff create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-italic.woff2 create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.eot create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.svg create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.ttf create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.woff create mode 100644 pagure/static/fonts/open-sans-v13-latin_latin-ext-regular.woff2 create mode 100644 pagure/static/hack_fonts/css/hack-extended.min.css create mode 100755 pagure/static/hack_fonts/fonts/eot/hack-bold-webfont.eot create mode 100755 pagure/static/hack_fonts/fonts/eot/hack-bolditalic-webfont.eot create mode 100755 pagure/static/hack_fonts/fonts/eot/hack-italic-webfont.eot create mode 100755 pagure/static/hack_fonts/fonts/eot/hack-regular-webfont.eot create mode 100755 pagure/static/hack_fonts/fonts/eot/latin/hack-bold-latin-webfont.eot create mode 100755 pagure/static/hack_fonts/fonts/eot/latin/hack-bolditalic-latin-webfont.eot create mode 100755 pagure/static/hack_fonts/fonts/eot/latin/hack-italic-latin-webfont.eot create mode 100755 pagure/static/hack_fonts/fonts/eot/latin/hack-regular-latin-webfont.eot create mode 100755 pagure/static/hack_fonts/fonts/svg/hack-bold-webfont.svg create mode 100755 pagure/static/hack_fonts/fonts/svg/hack-bolditalic-webfont.svg create mode 100755 pagure/static/hack_fonts/fonts/svg/hack-italic-webfont.svg create mode 100755 pagure/static/hack_fonts/fonts/svg/hack-regular-webfont.svg create mode 100755 pagure/static/hack_fonts/fonts/svg/latin/hack-bold-latin-webfont.svg create mode 100755 pagure/static/hack_fonts/fonts/svg/latin/hack-bolditalic-latin-webfont.svg create mode 100755 pagure/static/hack_fonts/fonts/svg/latin/hack-italic-latin-webfont.svg create mode 100755 pagure/static/hack_fonts/fonts/svg/latin/hack-regular-latin-webfont.svg create mode 100755 pagure/static/hack_fonts/fonts/web-ttf/hack-bold-webfont.ttf create mode 100755 pagure/static/hack_fonts/fonts/web-ttf/hack-bolditalic-webfont.ttf create mode 100755 pagure/static/hack_fonts/fonts/web-ttf/hack-italic-webfont.ttf create mode 100755 pagure/static/hack_fonts/fonts/web-ttf/hack-regular-webfont.ttf create mode 100755 pagure/static/hack_fonts/fonts/web-ttf/latin/hack-bold-latin-webfont.ttf create mode 100755 pagure/static/hack_fonts/fonts/web-ttf/latin/hack-bolditalic-latin-webfont.ttf create mode 100755 pagure/static/hack_fonts/fonts/web-ttf/latin/hack-italic-latin-webfont.ttf create mode 100755 pagure/static/hack_fonts/fonts/web-ttf/latin/hack-regular-latin-webfont.ttf create mode 100755 pagure/static/hack_fonts/fonts/woff/hack-bold-webfont.woff create mode 100755 pagure/static/hack_fonts/fonts/woff/hack-bolditalic-webfont.woff create mode 100755 pagure/static/hack_fonts/fonts/woff/hack-italic-webfont.woff create mode 100755 pagure/static/hack_fonts/fonts/woff/hack-regular-webfont.woff create mode 100755 pagure/static/hack_fonts/fonts/woff/latin/hack-bold-latin-webfont.woff create mode 100755 pagure/static/hack_fonts/fonts/woff/latin/hack-bolditalic-latin-webfont.woff create mode 100755 pagure/static/hack_fonts/fonts/woff/latin/hack-italic-latin-webfont.woff create mode 100755 pagure/static/hack_fonts/fonts/woff/latin/hack-regular-latin-webfont.woff create mode 100755 pagure/static/hack_fonts/fonts/woff2/hack-bold-webfont.woff2 create mode 100755 pagure/static/hack_fonts/fonts/woff2/hack-bolditalic-webfont.woff2 create mode 100755 pagure/static/hack_fonts/fonts/woff2/hack-italic-webfont.woff2 create mode 100755 pagure/static/hack_fonts/fonts/woff2/hack-regular-webfont.woff2 create mode 100755 pagure/static/hack_fonts/fonts/woff2/latin/hack-bold-latin-webfont.woff2 create mode 100755 pagure/static/hack_fonts/fonts/woff2/latin/hack-bolditalic-latin-webfont.woff2 create mode 100755 pagure/static/hack_fonts/fonts/woff2/latin/hack-italic-latin-webfont.woff2 create mode 100755 pagure/static/hack_fonts/fonts/woff2/latin/hack-regular-latin-webfont.woff2 create mode 100644 pagure/static/images/link.png create mode 100644 pagure/static/images/spinner.gif create mode 100755 pagure/static/images/users.png create mode 100644 pagure/static/issue_ev.js create mode 100644 pagure/static/jquery-1.10.2.js create mode 100644 pagure/static/jquery-ui-1.11.2.custom.min.js create mode 100644 pagure/static/jquery.dotdotdot.min.js create mode 100644 pagure/static/open_iconic_1.1.0/css/open-iconic.min.css create mode 100644 pagure/static/open_iconic_1.1.0/fonts/open-iconic.eot create mode 100644 pagure/static/open_iconic_1.1.0/fonts/open-iconic.otf create mode 100644 pagure/static/open_iconic_1.1.0/fonts/open-iconic.svg create mode 100644 pagure/static/open_iconic_1.1.0/fonts/open-iconic.ttf create mode 100644 pagure/static/open_iconic_1.1.0/fonts/open-iconic.woff create mode 100644 pagure/static/pagure-logo.png create mode 100644 pagure/static/pagure.css create mode 100644 pagure/static/request_ev.js create mode 100644 pagure/static/selectize.bootstrap3.css create mode 100644 pagure/static/selectize.min.js create mode 100644 pagure/static/stupidtable.min.js create mode 100644 pagure/static/toggle.css create mode 100644 pagure/static/upload.js create mode 100644 pagure/templates/_browseheader.html create mode 100644 pagure/templates/_formhelper.html create mode 100644 pagure/templates/_render_repo.html create mode 100644 pagure/templates/activity.html create mode 100644 pagure/templates/add_group.html create mode 100644 pagure/templates/add_group_project.html create mode 100644 pagure/templates/add_token.html create mode 100644 pagure/templates/add_user.html create mode 100644 pagure/templates/admin_index.html create mode 100644 pagure/templates/api.html create mode 100644 pagure/templates/comment_update.html create mode 100644 pagure/templates/commit.html create mode 100644 pagure/templates/commits.html create mode 100644 pagure/templates/doc_ssh_keys.html create mode 100644 pagure/templates/docs.html create mode 100644 pagure/templates/edit_file.html create mode 100644 pagure/templates/edit_tag.html create mode 100644 pagure/templates/fatal_error.html create mode 100644 pagure/templates/file.html create mode 100644 pagure/templates/forks.html create mode 100644 pagure/templates/group_info.html create mode 100644 pagure/templates/group_list.html create mode 100644 pagure/templates/index.html create mode 100644 pagure/templates/index_auth.html create mode 100644 pagure/templates/issue.html create mode 100644 pagure/templates/issues.html create mode 100644 pagure/templates/login/login.html create mode 100644 pagure/templates/login/password_change.html create mode 100644 pagure/templates/login/password_recover.html create mode 100644 pagure/templates/login/password_reset.html create mode 100644 pagure/templates/login/user_new.html create mode 100644 pagure/templates/master.html create mode 100644 pagure/templates/new_issue.html create mode 100644 pagure/templates/new_project.html create mode 100644 pagure/templates/new_release.html create mode 100644 pagure/templates/not_found.html create mode 100644 pagure/templates/plugin.html create mode 100644 pagure/templates/pull_request.html create mode 100644 pagure/templates/pull_request_comment.html create mode 100644 pagure/templates/pull_request_title.html create mode 100644 pagure/templates/releases.html create mode 100644 pagure/templates/remote_pull_request.html create mode 100644 pagure/templates/repo_info.html create mode 100644 pagure/templates/repo_master.html create mode 100644 pagure/templates/requests.html create mode 100644 pagure/templates/roadmap.html create mode 100644 pagure/templates/settings.html create mode 100644 pagure/templates/unauthorized.html create mode 100644 pagure/templates/user_emails.html create mode 100644 pagure/templates/user_info.html create mode 100644 pagure/templates/user_list.html create mode 100644 pagure/templates/user_requests.html create mode 100644 pagure/templates/user_settings.html create mode 100644 pagure/ui/__init__.py create mode 100644 pagure/ui/admin.py create mode 100644 pagure/ui/app.py create mode 100644 pagure/ui/filters.py create mode 100644 pagure/ui/fork.py create mode 100644 pagure/ui/groups.py create mode 100644 pagure/ui/issues.py create mode 100644 pagure/ui/login.py create mode 100644 pagure/ui/plugins.py create mode 100644 pagure/ui/repo.py create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tests/__init__.py create mode 100644 tests/placebo.png create mode 100644 tests/test_config create mode 100644 tests/test_pagure_flask_api.py create mode 100644 tests/test_pagure_flask_api_auth.py create mode 100644 tests/test_pagure_flask_api_fork.py create mode 100644 tests/test_pagure_flask_api_issue.py create mode 100644 tests/test_pagure_flask_api_project.py create mode 100644 tests/test_pagure_flask_docs.py create mode 100644 tests/test_pagure_flask_dump_load_ticket.py create mode 100644 tests/test_pagure_flask_internal.py create mode 100644 tests/test_pagure_flask_ui_admin.py create mode 100644 tests/test_pagure_flask_ui_app.py create mode 100644 tests/test_pagure_flask_ui_fork.py create mode 100644 tests/test_pagure_flask_ui_groups.py create mode 100644 tests/test_pagure_flask_ui_issues.py create mode 100644 tests/test_pagure_flask_ui_login.py create mode 100644 tests/test_pagure_flask_ui_no_master_branch.py create mode 100644 tests/test_pagure_flask_ui_plugins.py create mode 100644 tests/test_pagure_flask_ui_plugins_fedmsg.py create mode 100644 tests/test_pagure_flask_ui_plugins_irc.py create mode 100644 tests/test_pagure_flask_ui_plugins_mail.py create mode 100644 tests/test_pagure_flask_ui_plugins_noff.py create mode 100644 tests/test_pagure_flask_ui_plugins_pagure_hook.py create mode 100644 tests/test_pagure_flask_ui_plugins_pagure_request_hook.py create mode 100644 tests/test_pagure_flask_ui_plugins_pagure_ticket_hook.py create mode 100644 tests/test_pagure_flask_ui_plugins_rtd_hook.py create mode 100644 tests/test_pagure_flask_ui_plugins_unsigned.py create mode 100644 tests/test_pagure_flask_ui_priorities.py create mode 100644 tests/test_pagure_flask_ui_repo.py create mode 100644 tests/test_pagure_flask_ui_repo_slash_name.py create mode 100644 tests/test_pagure_flask_ui_roadmap.py create mode 100644 tests/test_pagure_flask_ui_slash_branch_name.py create mode 100644 tests/test_pagure_lib.py create mode 100644 tests/test_pagure_lib_git.py create mode 100644 tests/test_pagure_lib_git_get_tags_objects.py create mode 100644 tests/test_pagure_lib_link.py create mode 100644 tests/test_pagure_lib_login.py create mode 100644 tests/test_pagure_lib_model.py create mode 100644 tests/test_zzz_pagure_flask_ui_old_commit.py create mode 100644 webhook-server/pagure-webhook-server.py create mode 100644 webhook-server/pagure_webhook.service diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..f687a3c --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,10 @@ +include LICENSE README.rst requirements.txt UPGRADING.rst +include createdb.py +recursive-include pagure * +recursive-include files * +recursive-include milters * +recursive-include tests * +recursive-include doc * +recursive-include alembic * +recursive-include ev-server * +recursive-include webhook-server * diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..1423d67 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,11 @@ +Metadata-Version: 1.1 +Name: pagure +Version: 2.3.4 +Summary: A light-weight git-centered forge based on pygit2.. +Home-page: https://fedorahosted.org/pagure/ +Author: Pierre-Yves Chibon +Author-email: pingou@pingoured.fr +License: GPLv2+ +Download-URL: https://fedorahosted.org/releases/p/r/pagure/ +Description: UNKNOWN +Platform: UNKNOWN diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..d895e7c --- /dev/null +++ b/README.rst @@ -0,0 +1,81 @@ +Pagure +====== + +:Author: Pierre-Yves Chibon + + +Pagure is a git-centered forge, python based using pygit2. + +With pagure you can host your project with its documentation, let your users +report issues or request enhancements using the ticketing system and build your +community of contributors by allowing them to fork your projects and contribute +to it via the now-popular pull-request mechanism. + + +Homepage: https://pagure.io/pagure + +See it at work: https://pagure.io + + +Playground version: https://stg.pagure.io + + + +Get it running +============== + +* Install the needed system libraries:: + + sudo dnf install git python-virtualenv libgit2-devel \ + libjpeg-devel gcc libffi-devel redhat-rpm-config + + .. note:: Do note the version of libgit2 that you install, for example + in ``libgit2-0.23.4-1`` you need to keep in mind the ``0.23`` + +* Retrieve the sources:: + + git clone https://pagure.io/pagure.git + cd pagure + +* Install dependencies + + * create the virtualenv:: + + virtualenv pagure_env + source ./pagure_env/bin/activate + + * Install the correct version of pygit2:: + + pip install pygit2==.* + + So in our example:: + + pip install pygit2==0.23.* + + * Install the rest of the dependencies:: + + pip install -r requirements.txt + + +* Create the folder that will receive the projects, forks, docs, requests and + tickets' git repo:: + + mkdir {repos,docs,forks,tickets,requests} + + +* Create the inital database scheme:: + + python createdb.py + + +* Run it:: + + ./runserver.py + + +* To get some profiling information you can also run it as:: + + ./runserver.py --profile + + +This will launch the application at http://127.0.0.1:5000 diff --git a/UPGRADING.rst b/UPGRADING.rst new file mode 100644 index 0000000..3ebb508 --- /dev/null +++ b/UPGRADING.rst @@ -0,0 +1,187 @@ +Upgrading Pagure +================ + + +2.3.4 +----- + +Release 2.3.4 contains an important security fix, blocking a source of XSS +attack. (CVE-2016-1000037) + + + +From 2.2 to 2.3 +--------------- + +2.3 brings a few changes impacting the database scheme, including a new +`duplicate` status for tickets, a feature allowing one to `watch` or +`unwatch` a project and notifications on tickets as exist on pull-requests. + +Therefore, when upgrading from 2.2.x to 2.3, you will have to : + +* Create the new DB tables and the new status field using the ``createdb.py`` script. + +* Update the database schame using alembic: ``alembic upgrade head`` + +This update also brings a new configuration key: + +* ``PAGURE_ADMIN_USERS`` allows to mark some users as instance-wide admins, giving + them full access to every projects, private or not. This feature can then be + used as a way to clean spams. +* ``SMTP_PORT`` allows to specify the port to use when contacting the SMTP + server +* ``SMTP_SSL`` allows to specify whether to use SSL when contacting the SMTP + server +* ``SMTP_USERNAME`` and ``SMTP_PASSWORD`` if provided together allow to contact + an SMTP requiring authentication. + +In this update is also added the script ``api_key_expire_mail.py`` meant to be +run by a daily cron job and warning users when their API token is nearing its +expiration date. + + + +2.2.2 +----- + +Release 2.2.2 contains an important security fix, blocking a source of XSS +attack. + + + +From 2.1 to 2.2 +--------------- + +2.2 brings a number of bug fixes and a few improvements. + +One of the major changes impacts the databases where we must change some of the +table so that the foreign key cascade on delete (fixes deleting a project when a +few plugins were activated). + +When upgrading for 2.1 to 2.2 all you will have to do is: + +* Update the database scheme using alembic: ``alembic upgrade head`` + +.. note:: If you run another database system than PostgreSQL the alembic + revision ``317a285e04a8_delete_hooks.py`` will require adjustment as the + foreign key constraints are named and the names are driver dependant. + + + +From 2.0 to 2.1 +--------------- + +2.1 brings its usual flow of improvements and bug fixes. + +When upgrading from 2.0.x to 2.1 all you will have to: + +* Update the database schame using alembic: ``alembic upgrade head`` + + + +From 1.x to 2.0 +--------------- + +As the version change indicates, 2.0 brings quite a number of changes, +including some that are not backward compatible. + +When upgrading to 2.0 you will have to: + +* Update the database schema using alembic: ``alembic upgrade head`` + +* Create the new DB tables so that the new plugins work using the + ``createdb.py`` script + +* Move the forks git repo + +Forked git repos are now located under the same folder as the regular git +repos, just under a ``forks/`` subfolder. +So the structure changes from: :: + + repos/ + ├── foo.git + └── bar.git + + forks/ + ├── patrick/ + │  ├── test.git + │  └── ipsilon.git + └── pingou/ + ├── foo.git + └── bar.git + +to: :: + + repos/ + ├── foo.git + ├── bar.git + └── forks/ + ├── patrick/ + │  ├── test.git + │  └── ipsilon.git + └── pingou/ + ├── foo.git + └── bar.git + +So the entire ``forks`` folder is moved under the ``repos`` folder where +the other repositories are, containing the sources of the projects. + + +Git repos for ``tickets``, ``requests`` and ``docs`` will be trickier to +move as the structure changes from: :: + + tickets/ + ├── foo.git + ├── bar.git + ├── patrick/ + │  ├── test.git + │  └── ipsilon.git + └── pingou/ + ├── foo.git + └── bar.git + +to: :: + + tickets/ + ├── foo.git + ├── bar.git + └── forks/ + ├── patrick/ + │  ├── test.git + │  └── ipsilon.git + └── pingou/ + ├── foo.git + └── bar.git + +Same for the ``requests`` and the ``docs`` git repos. + +As you can see in the ``tickets``, ``requests`` and ``docs`` folders there +are two types of folders, git repos which are folder with a name ending +with ``.git``, and folder corresponding to usernames. These last ones are +the ones to be moved into a subfolder ``forks/``. + +This can be done using something like: :: + + mkdir forks + for i in `ls -1 |grep -v '\.git'`; do mv $i forks/; done + +* Re-generate the gitolite configuration. + +This can be done via the ``Re-generate gitolite ACLs file`` button in the +admin page. + +* Keep URLs backward compatible + +The support of pseudo-namespace in pagure 2.0 has required some changes +to the URL schema: +https://pagure.io/pagure/053d8cc95fcd50c23a8b0a7f70e55f8d1cc7aebb +became: +https://pagure.io/pagure/c/053d8cc95fcd50c23a8b0a7f70e55f8d1cc7aebb +(Note the added /c/ in it) + +We introduced a backward compatibility fix for this. + +This fix is however *disabled* by default so if you wish to keep the URLs +valid, you will need to adjust you configuration file to include: :: + + OLD_VIEW_COMMIT_ENABLED = True diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 0000000..712b616 --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,71 @@ +from __future__ import with_statement +from alembic import context +from sqlalchemy import engine_from_config, pool +from logging.config import fileConfig + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure(url=url, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool) + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() + diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 0000000..9570201 --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,22 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision} +Create Date: ${create_date} + +""" + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/alembic/versions/15ea3c2cf83d_pr_comment_editing.py b/alembic/versions/15ea3c2cf83d_pr_comment_editing.py new file mode 100644 index 0000000..ad3eedc --- /dev/null +++ b/alembic/versions/15ea3c2cf83d_pr_comment_editing.py @@ -0,0 +1,45 @@ +"""Adding column to store edited_by and edited_on a PR comment + +Revision ID: 15ea3c2cf83d +Revises: 1cd0a853c697 +Create Date: 2015-11-09 16:18:47.192088 + +""" + +# revision identifiers, used by Alembic. +revision = '15ea3c2cf83d' +down_revision = '1cd0a853c697' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the columns editor_id and edited_on to the table + pull_request_comments. + ''' + + op.add_column( + 'pull_request_comments', + sa.Column( + 'editor_id', + sa.Integer, + sa.ForeignKey('users.id', onupdate='CASCADE'), + nullable=True) + ) + + op.add_column( + 'pull_request_comments', + sa.Column( + 'edited_on', + sa.DateTime, + nullable=True) + ) + + +def downgrade(): + ''' Remove the columns editor_id and edited_on from the table + pull_request_comments. + ''' + op.drop_column('pull_request_comments', 'editor_id') + op.drop_column('pull_request_comments', 'edited_on') diff --git a/alembic/versions/1b6d7dc5600a_versioning_passwords.py b/alembic/versions/1b6d7dc5600a_versioning_passwords.py new file mode 100644 index 0000000..e1c0a13 --- /dev/null +++ b/alembic/versions/1b6d7dc5600a_versioning_passwords.py @@ -0,0 +1,38 @@ +"""versioning_passwords + +Revision ID: 1b6d7dc5600a +Revises: 3b441ef4e928 +Create Date: 2016-01-13 07:57:23.465676 + +""" + +# revision identifiers, used by Alembic. +revision = '1b6d7dc5600a' +down_revision = '3b441ef4e928' + +from alembic import op +import sqlalchemy as sa +import sqlalchemy.orm + +try: + from pagure.lib import model +except ImportError: + import sys + sys.path.insert(0, '.') + from pagure.lib import model + + +def upgrade(): + engine = op.get_bind() + Session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker()) + Session.configure(bind=engine) + session = Session() + for user in session.query(model.User).filter( + model.User.password != None).all(): + user.password = '$1$%s' % user.password + session.add(user) + session.commit() + + +def downgrade(): + raise ValueError("Password can not be downgraded") diff --git a/alembic/versions/1cd0a853c697_add_closed_at_field_in_pr.py b/alembic/versions/1cd0a853c697_add_closed_at_field_in_pr.py new file mode 100644 index 0000000..d6eef26 --- /dev/null +++ b/alembic/versions/1cd0a853c697_add_closed_at_field_in_pr.py @@ -0,0 +1,37 @@ +"""Add closed_at field in PR + + +Revision ID: 1cd0a853c697 +Revises: 6190226bed0 +Create Date: 2015-10-02 09:32:15.370676 + +""" + +# revision identifiers, used by Alembic. +revision = '1cd0a853c697' +down_revision = '6190226bed0' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column closed_at to the table pull_requests. + ''' + op.add_column( + 'pull_requests', + sa.Column( + 'closed_at', + sa.DateTime, + nullable=True, + ) + ) + + op.execute('''UPDATE "pull_requests" SET closed_at=date_created ''' + '''WHERE STATUS != 'Open';''') + + +def downgrade(): + ''' Remove the column closed_at from the table pull_requests. + ''' + op.drop_column('pull_requests', 'closed_at') diff --git a/alembic/versions/1f3de3853a1a_add_the_tree_id_column_to_pr_inline_.py b/alembic/versions/1f3de3853a1a_add_the_tree_id_column_to_pr_inline_.py new file mode 100644 index 0000000..eb41dcd --- /dev/null +++ b/alembic/versions/1f3de3853a1a_add_the_tree_id_column_to_pr_inline_.py @@ -0,0 +1,29 @@ +"""Add the tree_id column to PR inline comments + +Revision ID: 1f3de3853a1a +Revises: 58e60d869326 +Create Date: 2016-02-22 16:13:59.943083 + +""" + +# revision identifiers, used by Alembic. +revision = '1f3de3853a1a' +down_revision = '58e60d869326' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column tree_id to the table pull_request_comments. + ''' + op.add_column( + 'pull_request_comments', + sa.Column('tree_id', sa.String(40), nullable=True) + ) + + +def downgrade(): + ''' Remove the column tree_id from the table pull_request_comments. + ''' + op.drop_column('pull_request_comments', 'tree_id') diff --git a/alembic/versions/22db0a833d35_add_notifications_to_tickets.py b/alembic/versions/22db0a833d35_add_notifications_to_tickets.py new file mode 100644 index 0000000..20bfad7 --- /dev/null +++ b/alembic/versions/22db0a833d35_add_notifications_to_tickets.py @@ -0,0 +1,33 @@ +"""Add notifications to tickets + +Revision ID: 22db0a833d35 +Revises: 317a285e04a8 +Create Date: 2016-06-27 16:10:33.395495 + +""" + +# revision identifiers, used by Alembic. +revision = '22db0a833d35' +down_revision = '317a285e04a8' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column notification to the table issue_comments. + ''' + op.add_column( + 'issue_comments', + sa.Column('notification', sa.Boolean, default=False, nullable=True) + ) + op.execute('''UPDATE "issue_comments" SET notification=False;''') + op.alter_column( + 'issue_comments', 'notification', + nullable=False, existing_nullable=True) + + +def downgrade(): + ''' Remove the column notification from the table issue_comments. + ''' + op.drop_column('issue_comments', 'notification') diff --git a/alembic/versions/257a7ce22682_add_the_remote_git_entry.py b/alembic/versions/257a7ce22682_add_the_remote_git_entry.py new file mode 100644 index 0000000..623568c --- /dev/null +++ b/alembic/versions/257a7ce22682_add_the_remote_git_entry.py @@ -0,0 +1,46 @@ +"""Add the remote_git entry + +Revision ID: 257a7ce22682 +Revises: 36116bb7a69b +Create Date: 2015-07-21 14:26:23.989220 + +""" + +# revision identifiers, used by Alembic. +revision = '257a7ce22682' +down_revision = '36116bb7a69b' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column remote_git to the table pull_requests and make the + project_id_from field nullable. + ''' + op.add_column( + 'pull_requests', + sa.Column('remote_git', sa.Text, nullable=True) + ) + op.alter_column( + 'pull_requests', + column_name='project_id_from', + nullable=True, + existing_nullable=False) + op.create_check_constraint( + "ck_lcl_or_remo_pr", + "pull_requests", + 'NOT(project_id_from IS NULL AND remote_git IS NULL)' + ) + + +def downgrade(): + ''' Remove the column remote_git from the table pull_requests and make + the project_id_from field not nullable. + ''' + op.drop_column('pull_requests', 'remote_git') + op.alter_column( + 'pull_requests', + column_name='project_id_from', + nullable=False, + existing_nullable=True) diff --git a/alembic/versions/298891e63039_change_the_status_of_pull_requests.py b/alembic/versions/298891e63039_change_the_status_of_pull_requests.py new file mode 100644 index 0000000..beb05b9 --- /dev/null +++ b/alembic/versions/298891e63039_change_the_status_of_pull_requests.py @@ -0,0 +1,60 @@ +"""Change the status of pull_requests + + +Revision ID: 298891e63039 +Revises: 3c25e14b855b +Create Date: 2015-06-08 13:06:11.938966 + +""" + +# revision identifiers, used by Alembic. +revision = '298891e63039' +down_revision = '3c25e14b855b' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Adjust the status column of the pull_requests table. + ''' + op.add_column( + 'pull_requests', + sa.Column( + '_status', sa.Text, + sa.ForeignKey( + 'status_pull_requests.status', onupdate='CASCADE'), + default='Open', + nullable=True) + ) + + op.execute('''UPDATE "pull_requests" ''' + '''SET _status='Open' WHERE status=TRUE;''') + op.execute('''UPDATE "pull_requests" ''' + '''SET _status='Merged' WHERE status=FALSE;''') + + op.drop_column('pull_requests', 'status') + op.alter_column( + 'pull_requests', + column_name='_status', new_column_name='status', + nullable=False, existing_nullable=True) + + +def downgrade(): + ''' Revert the status column of the pull_requests table. + ''' + op.add_column( + 'pull_requests', + sa.Column( + '_status', sa.Boolean, default=True, nullable=True) + ) + op.execute('''UPDATE "pull_requests" ''' + '''SET _status=TRUE WHERE status='Open';''') + op.execute('''UPDATE "pull_requests" ''' + '''SET _status=FALSE WHERE status!='Open';''') + + op.drop_column('pull_requests', 'status') + op.alter_column( + 'pull_requests', + column_name='_status', new_column_name='status', + nullable=False, existing_nullable=True) diff --git a/alembic/versions/2aa7b3958bc5_add_the_milestones_column.py b/alembic/versions/2aa7b3958bc5_add_the_milestones_column.py new file mode 100644 index 0000000..5a00dbb --- /dev/null +++ b/alembic/versions/2aa7b3958bc5_add_the_milestones_column.py @@ -0,0 +1,31 @@ +"""Add the milestones column + +Revision ID: 2aa7b3958bc5 +Revises: 443e090da188 +Create Date: 2016-05-03 15:59:04.992414 + +""" + +# revision identifiers, used by Alembic. +revision = '2aa7b3958bc5' +down_revision = '443e090da188' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column _milestones to the table projects + and the column milestone to the table issues. + ''' + op.add_column( + 'projects', + sa.Column('_milestones', sa.Text, nullable=True) + ) + + +def downgrade(): + ''' Drop the column _milestones from the table projects + and the column milestone from the table issues. + ''' + op.drop_column('projects', '_milestones') diff --git a/alembic/versions/317a285e04a8_delete_hooks.py b/alembic/versions/317a285e04a8_delete_hooks.py new file mode 100644 index 0000000..2ee64e2 --- /dev/null +++ b/alembic/versions/317a285e04a8_delete_hooks.py @@ -0,0 +1,59 @@ +"""Delete hooks + +Revision ID: 317a285e04a8 +Revises: 2aa7b3958bc5 +Create Date: 2016-05-30 11:28:48.512577 + +""" + +# revision identifiers, used by Alembic. +revision = '317a285e04a8' +down_revision = '2aa7b3958bc5' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + """ Alter the hooks table to update the foreign key to cascade on delete. + """ + + for table in [ + 'hook_fedmsg', 'hook_irc', 'hook_mail', + 'hook_pagure_force_commit', 'hook_pagure', 'hook_pagure_requests', + 'hook_pagure_tickets', 'hook_pagure_unsigned_commit', 'hook_rtd', + ]: + op.drop_constraint( + '%s_project_id_fkey' % table, + table, + type_='foreignkey') + op. create_foreign_key( + name='%s_project_id_fkey' % table, + source_table=table, + referent_table='projects', + local_cols=['project_id'], + remote_cols=['id'], + onupdate='cascade', + ondelete='cascade', + ) + + op.drop_constraint( + 'projects_groups_project_id_fkey', + 'projects_groups', + type_='foreignkey') + op. create_foreign_key( + name='projects_groups_project_id_fkey', + source_table='projects_groups', + referent_table='projects', + local_cols=['project_id'], + remote_cols=['id'], + onupdate='cascade', + ondelete='cascade', + ) + + + +def downgrade(): + """ Alter the hooks table to update the foreign key to undo the cascade + on delete. + """ diff --git a/alembic/versions/36116bb7a69b_add_the_url_field_to_project.py b/alembic/versions/36116bb7a69b_add_the_url_field_to_project.py new file mode 100644 index 0000000..27784ef --- /dev/null +++ b/alembic/versions/36116bb7a69b_add_the_url_field_to_project.py @@ -0,0 +1,29 @@ +"""Add the url field to project + +Revision ID: 36116bb7a69b +Revises: abc71fd60fa +Create Date: 2015-06-11 12:36:33.544046 + +""" + +# revision identifiers, used by Alembic. +revision = '36116bb7a69b' +down_revision = 'abc71fd60fa' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column url to the table projects. + ''' + op.add_column( + 'projects', + sa.Column('url', sa.Text, nullable=True) + ) + + +def downgrade(): + ''' Remove the column merge_status from the table projects. + ''' + op.drop_column('projects', 'url') diff --git a/alembic/versions/3b441ef4e928_comment_editing_issue.py b/alembic/versions/3b441ef4e928_comment_editing_issue.py new file mode 100644 index 0000000..60f5e87 --- /dev/null +++ b/alembic/versions/3b441ef4e928_comment_editing_issue.py @@ -0,0 +1,44 @@ +"""Adding column to store edited_by and edited_on a issue comment + +Revision ID: 3b441ef4e928 +Revises: 15ea3c2cf83d +Create Date: 2015-12-03 12:34:28.316699 + +""" + +# revision identifiers, used by Alembic. +revision = '3b441ef4e928' +down_revision = '15ea3c2cf83d' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the columns editor_id and edited_on to the table issue_comments. + ''' + + op.add_column( + 'issue_comments', + sa.Column( + 'editor_id', + sa.Integer, + sa.ForeignKey('users.id', onupdate='CASCADE'), + nullable=True) + ) + + op.add_column( + 'issue_comments', + sa.Column( + 'edited_on', + sa.DateTime, + nullable=True) + ) + + +def downgrade(): + ''' Remove the columns editor_id and edited_on from the table + issue_comments. + ''' + op.drop_column('issue_comments', 'editor_id') + op.drop_column('issue_comments', 'edited_on') diff --git a/alembic/versions/3c25e14b855b_add_an_avatar_email_for_project.py b/alembic/versions/3c25e14b855b_add_an_avatar_email_for_project.py new file mode 100644 index 0000000..407f2d4 --- /dev/null +++ b/alembic/versions/3c25e14b855b_add_an_avatar_email_for_project.py @@ -0,0 +1,29 @@ +"""add an avatar email for project + +Revision ID: 3c25e14b855b +Revises: b5efae6bb23 +Create Date: 2015-06-08 12:05:13.832348 + +""" + +# revision identifiers, used by Alembic. +revision = '3c25e14b855b' +down_revision = 'b5efae6bb23' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column merge_status to the table projects. + ''' + op.add_column( + 'projects', + sa.Column('avatar_email', sa.Text, nullable=True) + ) + + +def downgrade(): + ''' Remove the column merge_status from the table projects. + ''' + op.drop_column('projects', 'avatar_email') diff --git a/alembic/versions/43df5e588a87_add_closed_at_attribute_in_issues.py b/alembic/versions/43df5e588a87_add_closed_at_attribute_in_issues.py new file mode 100644 index 0000000..5ceb1da --- /dev/null +++ b/alembic/versions/43df5e588a87_add_closed_at_attribute_in_issues.py @@ -0,0 +1,27 @@ +"""add_closed_at_attribute_in_issues + +Revision ID: 43df5e588a87 +Revises: 22db0a833d35 +Create Date: 2016-06-28 22:59:36.653905 + +""" + +# revision identifiers, used by Alembic. +revision = '43df5e588a87' +down_revision = '22db0a833d35' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add closed_at column in issues table ''' + op.add_column( + 'issues', + sa.Column('closed_at', sa.DateTime, nullable=True) + ) + + +def downgrade(): + ''' Remove the closed_at column in issues table ''' + op.drop_column('issues', 'closed_at') diff --git a/alembic/versions/443e090da188_up_to_255_characters_for_project_name.py b/alembic/versions/443e090da188_up_to_255_characters_for_project_name.py new file mode 100644 index 0000000..5518b30 --- /dev/null +++ b/alembic/versions/443e090da188_up_to_255_characters_for_project_name.py @@ -0,0 +1,32 @@ +"""up to 255 characters for project.name + +Revision ID: 443e090da188 +Revises: 496f7a700f2e +Create Date: 2016-04-20 17:57:36.385103 + +""" + +# revision identifiers, used by Alembic. +revision = '443e090da188' +down_revision = '496f7a700f2e' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.alter_column( + table_name='projects', + column_name='name', + type_=sa.String(255), + existing_type=sa.String(32) + ) + + +def downgrade(): + op.alter_column( + table_name='projects', + column_name='name', + type_=sa.String(32), + existing_type=sa.String(255) + ) diff --git a/alembic/versions/496f7a700f2e_add_priorities.py b/alembic/versions/496f7a700f2e_add_priorities.py new file mode 100644 index 0000000..21c7ca7 --- /dev/null +++ b/alembic/versions/496f7a700f2e_add_priorities.py @@ -0,0 +1,37 @@ +"""Add priorities + +Revision ID: 496f7a700f2e +Revises: 4cae55a80a42 +Create Date: 2016-03-24 12:19:34.298752 + +""" + +# revision identifiers, used by Alembic. +revision = '496f7a700f2e' +down_revision = '4cae55a80a42' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column _priorities to the table projects + and the column priority to the table issues. + ''' + op.add_column( + 'projects', + sa.Column('_priorities', sa.Text, nullable=True) + ) + + op.add_column( + 'issues', + sa.Column('priority', sa.Integer, nullable=True, default=None) + ) + + +def downgrade(): + ''' Drop the column _priorities from the table projects + and the column priority from the table issues. + ''' + op.drop_column('projects', '_priorities') + op.drop_column('issues', 'priority') diff --git a/alembic/versions/4cae55a80a42_add_the_initial_comment_on_the_pr_table.py b/alembic/versions/4cae55a80a42_add_the_initial_comment_on_the_pr_table.py new file mode 100644 index 0000000..c329944 --- /dev/null +++ b/alembic/versions/4cae55a80a42_add_the_initial_comment_on_the_pr_table.py @@ -0,0 +1,29 @@ +"""Add the initial_comment on the PR table + +Revision ID: 4cae55a80a42 +Revises: 1f3de3853a1a +Create Date: 2016-03-01 12:00:34.823097 + +""" + +# revision identifiers, used by Alembic. +revision = '4cae55a80a42' +down_revision = '1f3de3853a1a' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column initial_comment to the table pull_requests. + ''' + op.add_column( + 'pull_requests', + sa.Column('initial_comment', sa.Text, nullable=True) + ) + + +def downgrade(): + ''' Remove the column initial_comment from the table pull_requests. + ''' + op.drop_column('pull_requests', 'initial_comment') diff --git a/alembic/versions/58e60d869326_add_notification_bool_to_pr.py b/alembic/versions/58e60d869326_add_notification_bool_to_pr.py new file mode 100644 index 0000000..2c7603b --- /dev/null +++ b/alembic/versions/58e60d869326_add_notification_bool_to_pr.py @@ -0,0 +1,33 @@ +"""add notification bool to PR + +Revision ID: 58e60d869326 +Revises: 1b6d7dc5600a +Create Date: 2016-02-12 12:39:07.839530 + +""" + +# revision identifiers, used by Alembic. +revision = '58e60d869326' +down_revision = '1b6d7dc5600a' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column notification to the table pull_request_comments. + ''' + op.add_column( + 'pull_request_comments', + sa.Column('notification', sa.Boolean, default=False, nullable=True) + ) + op.execute('''UPDATE "pull_request_comments" SET notification=False;''') + op.alter_column( + 'pull_request_comments', 'notification', + nullable=False, existing_nullable=True) + + +def downgrade(): + ''' Remove the column notification from the table pull_request_comments. + ''' + op.drop_column('pull_request_comments', 'notification') diff --git a/alembic/versions/6190226bed0_add_the_updated_on_column_to_pull_.py b/alembic/versions/6190226bed0_add_the_updated_on_column_to_pull_.py new file mode 100644 index 0000000..6a362b6 --- /dev/null +++ b/alembic/versions/6190226bed0_add_the_updated_on_column_to_pull_.py @@ -0,0 +1,43 @@ +"""Add the updated_on column to pull-requests + +Revision ID: 6190226bed0 +Revises: 257a7ce22682 +Create Date: 2015-09-29 15:32:58.229183 + +""" + +# revision identifiers, used by Alembic. +revision = '6190226bed0' +down_revision = '257a7ce22682' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column updated_on to the table pull_requests. + ''' + op.add_column( + 'pull_requests', + sa.Column( + 'updated_on', + sa.DateTime, + nullable=True, + default=sa.func.now(), + onupdate=sa.func.now() + ) + ) + + op.execute('''UPDATE "pull_requests" SET updated_on=date_created;''') + + op.alter_column( + 'pull_requests', + column_name='updated_on', + nullable=False, + existing_nullable=True) + + +def downgrade(): + ''' Remove the column updated_on from the table pull_requests. + ''' + op.drop_column('pull_requests', 'updated_on') diff --git a/alembic/versions/abc71fd60fa_add_the_closed_by_column_to_pull_.py b/alembic/versions/abc71fd60fa_add_the_closed_by_column_to_pull_.py new file mode 100644 index 0000000..17e5ff6 --- /dev/null +++ b/alembic/versions/abc71fd60fa_add_the_closed_by_column_to_pull_.py @@ -0,0 +1,34 @@ +"""Add the closed_by column to pull_requests + + +Revision ID: abc71fd60fa +Revises: 298891e63039 +Create Date: 2015-06-08 16:06:18.017110 + +""" + +# revision identifiers, used by Alembic. +revision = 'abc71fd60fa' +down_revision = '298891e63039' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add the column merge_status to the table pull_requests. + ''' + op.add_column( + 'pull_requests', + sa.Column( + 'closed_by_id', + sa.Integer, + sa.ForeignKey('users.id', onupdate='CASCADE'), + ) + ) + + +def downgrade(): + ''' Remove the column merge_status from the table pull_requests. + ''' + op.drop_column('pull_requests', 'closed_by_id') diff --git a/alembic/versions/b5efae6bb23_add_merge_status_to_the_pull_requests_.py b/alembic/versions/b5efae6bb23_add_merge_status_to_the_pull_requests_.py new file mode 100644 index 0000000..aa12d31 --- /dev/null +++ b/alembic/versions/b5efae6bb23_add_merge_status_to_the_pull_requests_.py @@ -0,0 +1,35 @@ +"""Add merge status to the pull_requests table + +Revision ID: b5efae6bb23 +Revises: None +Create Date: 2015-06-02 16:30:06.199128 + +""" + +# revision identifiers, used by Alembic. +revision = 'b5efae6bb23' +down_revision = None + +from alembic import op +from sqlalchemy.dialects.postgresql import ENUM +import sqlalchemy as sa + + +# Sources for the code: https://bitbucket.org/zzzeek/alembic/issue/67 + +def upgrade(): + ''' Add the column merge_status to the table pull_requests. + ''' + enum = ENUM('NO_CHANGE', 'FFORWARD', 'CONFLICTS', 'MERGE', + name='merge_status_enum', create_type=False) + enum.create(op.get_bind(), checkfirst=False) + op.add_column( + 'pull_requests', + sa.Column('merge_status', enum, nullable=True) + ) + +def downgrade(): + ''' Remove the column merge_status from the table pull_requests. + ''' + ENUM(name="merge_status_enum").drop(op.get_bind(), checkfirst=False) + op.drop_column('pull_requests', 'merge_status') diff --git a/createdb.py b/createdb.py new file mode 100644 index 0000000..eb78c78 --- /dev/null +++ b/createdb.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python2 + +# These two lines are needed to run on EL6 +__requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4'] +import pkg_resources + +from pagure import APP +from pagure.lib import model + +model.create_tables( + APP.config['DB_URL'], + APP.config.get('PATH_ALEMBIC_INI', None), + acls=APP.config.get('ACLS', {}), + debug=True) diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..73658c9 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pagure.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pagure.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/pagure" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pagure" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/_static/overview.png b/doc/_static/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..32c8531b1ec05575694e2e786cbc34f65cee705b GIT binary patch literal 38621 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 literal 0 HcmV?d00001 diff --git a/doc/_static/overview_simple.png b/doc/_static/overview_simple.png new file mode 100644 index 0000000000000000000000000000000000000000..f898461a0ea0865087ee41052fb0adb160bb6ec9 GIT binary patch literal 18776 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 literal 0 HcmV?d00001 diff --git a/doc/_static/pagure.png b/doc/_static/pagure.png new file mode 100644 index 0000000000000000000000000000000000000000..cd8ed7f83ced43b44577dccaf7d5b5844bf11694 GIT binary patch literal 4336 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