├── .gitignore ├── .gitlab-ci.yml ├── COPYING ├── README.md ├── docs ├── .gitignore ├── Makefile ├── NEWS ├── TODO ├── conf.py ├── development.rst ├── examples │ ├── testing-checkpatch.py │ └── testing-show-series.py ├── images │ └── testing-ci-flow.png ├── index.rst ├── installation.rst ├── intro.rst ├── manual.rst ├── requirements-base.txt ├── requirements-dev-mysql.txt ├── requirements-dev-postgresql.txt ├── requirements-dev-sqlite.txt ├── requirements-dev.txt ├── requirements-prod-mysql.txt ├── requirements-prod-postgres.txt ├── rest.rst ├── symbols └── testing.rst ├── git-pw ├── git-pw └── requirements.txt ├── htdocs ├── css │ ├── bootstrap-datepicker3.min.css │ ├── bootstrap.min.css │ ├── jquery.dynatable.css │ ├── selectize.bootstrap3.css │ └── style.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── images │ ├── 16-em-cross.png │ └── 16-em-down.png └── js │ ├── bootstrap-datepicker.min.js │ ├── bootstrap.min.js │ ├── bundle.js │ ├── common.js │ ├── jquery-1.10.1.min.js │ ├── jquery.dynatable.js │ ├── jquery.stickytableheaders.min.js │ ├── jquery.tablednd.js │ ├── mustache.min.js │ ├── patchwork.js │ ├── selectize.min.js │ └── series-list.js ├── lib ├── apache2 │ ├── patchwork.wsgi │ └── patchwork.wsgi.conf ├── nginx │ └── patchwork.conf ├── packages │ ├── .gitignore │ └── jquery │ │ ├── README │ │ ├── jquery-1.10.1.min.js │ │ ├── jquery.dynatable.css │ │ ├── jquery.dynatable.js │ │ ├── jquery.stickytableheaders.min.js │ │ └── jquery.tablednd.js ├── sql │ ├── grant-all.mysql.sql │ ├── grant-all.postgres.sql │ └── migration │ │ ├── 001-hex-hash-types.sql │ │ ├── 002-extend-userpersonconfirmation-key-length.sql │ │ ├── 003-add-comment-parent.sql │ │ ├── 004-msgid-uniqueness.sql │ │ ├── 005-bundle-patch-ordering.sql │ │ ├── 007-patch-pull-requests.sql │ │ ├── 008-confirmations.sql │ │ ├── 009-drop-registrationprofile.sql │ │ ├── 010-optout-tables.sql │ │ ├── 011-patch-change-notifications.sql │ │ ├── 012-project-add-columns.sql │ │ ├── 013-bundle-names.sql │ │ ├── 014-cleanup-people.sql │ │ └── 015-add-patch-tags.sql ├── supervisor │ └── patchwork-celery-worker.conf └── uwsgi │ └── patchwork.ini ├── manage.py ├── patchwork ├── __init__.py ├── admin.py ├── bin │ ├── __init__.py │ ├── bash_completion │ ├── parsemail-batch.sh │ ├── parsemail.py │ ├── parsemail.sh │ ├── pwclient │ └── update-patchwork-status.py ├── celery.py ├── context_processors.py ├── email.py ├── fields.py ├── filters.py ├── fixtures │ ├── default_events.xml │ ├── default_projects.xml │ ├── default_states.xml │ ├── default_tags.xml │ └── test_data.xml ├── forms.py ├── lock.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── cron.py │ │ ├── rehash.py │ │ ├── retag.py │ │ └── retriageproject.py ├── middleware.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_fix_patch_state_default_values.py │ ├── 0003_series.py │ ├── 0004_project_git_send_email_only.py │ ├── 0005_event_eventlog.py │ ├── 0006_eventlog_parameters.py │ ├── 0007_multiple_projects_same_ml.py │ ├── 0008_test_results.py │ ├── 0009_test_results_mail.py │ ├── 0010_test_results_to_cc_list.py │ ├── 0011_test_results_updated_ts_fix.py │ ├── 0012_project_description.py │ ├── 0013_seriesrevision_test_state.py │ ├── 0014_last_revision.py │ ├── 0015_remove_version_n_patches.py │ ├── 0016_add_delegation_rule_model.py │ ├── 0017_series_meta.py │ ├── 0018_test_email_on_error.py │ ├── 0019_eventlog_patch.py │ ├── 0020_series_state.py │ ├── 0021_unselectable_maintainer_projects.py │ ├── 0022_add_test_result_state_info.py │ ├── 0023_fix_test_state_order.py │ ├── 0024_blank_test_state.py │ ├── 0025_patch_last_updated.py │ ├── 0026_event_series_not_mandatory.py │ ├── 0027_auto_20180116_0044.py │ ├── 0028_auto_20180220_2246.py │ ├── 0029_seriesrevision_is_rerun.py │ └── __init__.py ├── models.py ├── paginator.py ├── parser.py ├── permissions.py ├── serializers.py ├── settings │ ├── __init__.py │ ├── base.py │ ├── dev-sqlite.py │ ├── dev.py │ └── production.example.py ├── tasks.py ├── templates │ ├── emails │ │ ├── new_reviewer_notification.body.txt │ │ ├── new_reviewer_notification.subject.txt │ │ ├── previous_reviewer_notification.body.txt │ │ └── previous_reviewer_notification.subject.txt │ └── patchwork │ │ ├── activation_email.txt │ │ ├── activation_email_subject.txt │ │ ├── bundle.html │ │ ├── bundles.html │ │ ├── confirm-error.html │ │ ├── filters.html │ │ ├── help │ │ ├── about.html │ │ ├── index.html │ │ └── pwclient.html │ │ ├── list.html │ │ ├── login.html │ │ ├── mail-form.html │ │ ├── mail-settings.html │ │ ├── optin-request.html │ │ ├── optin-request.mail │ │ ├── optin.html │ │ ├── optout-request.html │ │ ├── optout-request.mail │ │ ├── optout.html │ │ ├── pagination.html │ │ ├── patch-change-notification-subject.text │ │ ├── patch-change-notification.mail │ │ ├── patch-list.html │ │ ├── patch.html │ │ ├── profile.html │ │ ├── project.html │ │ ├── projects.html │ │ ├── pwclient │ │ ├── pwclientrc │ │ ├── register.mail │ │ ├── registration-confirm.html │ │ ├── registration_form.html │ │ ├── series-list-table.html │ │ ├── series-list-templates.html │ │ ├── series-list.html │ │ ├── series.html │ │ ├── test-result.html │ │ ├── todo-list.html │ │ ├── todo-lists.html │ │ ├── user-link-confirm.html │ │ ├── user-link.html │ │ └── user-link.mail ├── templatetags │ ├── __init__.py │ ├── listurl.py │ ├── patch.py │ ├── person.py │ └── syntax.py ├── tests │ ├── __init__.py │ ├── browser.py │ ├── mail │ │ ├── 0001-git-pull-request.mbox │ │ ├── 0002-git-pull-request-wrapped.mbox │ │ ├── 0003-git-pull-request-with-diff.mbox │ │ ├── 0004-git-pull-request-git+ssh.mbox │ │ ├── 0005-git-pull-request-ssh.mbox │ │ ├── 0006-git-pull-request-http.mbox │ │ ├── 0007-cvs-format-diff.mbox │ │ ├── 0008-git-rename.mbox │ │ ├── 0009-git-rename-with-diff.mbox │ │ ├── 0010-invalid-charset.mbox │ │ ├── 0011-no-newline-at-end-of-file.mbox │ │ └── series │ │ │ ├── 0001-single-mail.mbox │ │ │ ├── 0010-multiple-mails-cover-letter.mbox │ │ │ ├── 0011-multiple-mails-cover-letter.mbox │ │ │ ├── 0012-multiple-mails-cover-letter.mbox │ │ │ ├── 0013-multiple-mails-cover-letter.mbox │ │ │ ├── 0014-multiple-mails-cover-letter.mbox │ │ │ ├── 0020-multiple-mails-no-cover-letter.mbox │ │ │ ├── 0021-multiple-mails-no-cover-letter.mbox │ │ │ ├── 0022-multiple-mails-no-cover-letter.mbox │ │ │ ├── 0030-patch-v2-in-reply.mbox │ │ │ ├── 0031-patch-v2-in-reply.mbox │ │ │ ├── 0032-patch-v2-in-reply.mbox │ │ │ └── 0033-patch-v2-in-reply.mbox │ ├── patches │ │ ├── 0001-add-line.patch │ │ └── 0002-utf-8.patch │ ├── test_bundles.py │ ├── test_comment.py │ ├── test_confirm.py │ ├── test_db.py │ ├── test_encodings.py │ ├── test_expiry.py │ ├── test_fields.py │ ├── test_filters.py │ ├── test_list.py │ ├── test_lock.py │ ├── test_mail_settings.py │ ├── test_mboxviews.py │ ├── test_notifications.py │ ├── test_patchparser.py │ ├── test_person.py │ ├── test_registration.py │ ├── test_rest.py │ ├── test_series.py │ ├── test_tags.py │ ├── test_updates.py │ ├── test_user.py │ ├── test_user_browser.py │ ├── test_xmlrpc.py │ └── utils.py ├── threadlocalrequest.py ├── urls.py ├── utils.py └── views │ ├── __init__.py │ ├── api.py │ ├── base.py │ ├── bundle.py │ ├── mail.py │ ├── patch.py │ ├── project.py │ ├── series.py │ ├── user.py │ └── xmlrpc.py ├── templates ├── 404.html ├── base.html └── registration │ ├── password_change_done.html │ ├── password_change_form.html │ ├── password_reset_complete.html │ ├── password_reset_confirm.html │ ├── password_reset_done.html │ ├── password_reset_email.html │ └── password_reset_form.html ├── tests ├── karma.conf.js ├── requirements.txt ├── test_all.sh ├── test_js.sh ├── test_jshint.sh ├── test_karma.sh └── test_patchwork.js ├── tools ├── patchwork-update-commits ├── post-receive.hook ├── run-devel.sh └── setup-devel.sh └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE! Don't add files that are generated in specific 3 | # subdirectories here. Add them in the ".gitignore" file 4 | # in that subdirectory instead. 5 | # 6 | # Normal rules 7 | # 8 | .* 9 | *.pyc 10 | *.patch 11 | 12 | # 13 | # Top-level generic files 14 | # 15 | tags 16 | TAGS 17 | !.gitignore 18 | !.gitlab-ci.yml 19 | 20 | # configuration files 21 | patchwork/settings/production.py 22 | 23 | # stgit generated dirs 24 | patches-* 25 | 26 | # quilt's files 27 | /patches 28 | /series 29 | 30 | # cscope files 31 | cscope.* 32 | 33 | *.orig 34 | *.rej 35 | 36 | # test artifacts 37 | /selenium.log 38 | /selenium_screenshots 39 | 40 | # created by tools/setup-devel.sh 41 | /venv 42 | /patchwork/settings/db.sqlite 43 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: python:3.6 2 | 3 | services: 4 | - mariadb:latest 5 | 6 | variables: 7 | PW_TEST_DB_HOST: 'mariadb' 8 | PW_TEST_DB_USER: 'root' 9 | MYSQL_DATABASE: 'patchwork' 10 | MYSQL_ROOT_PASSWORD: 'password' 11 | DEBIAN_FRONTEND: 'noninteractive' 12 | CHROME_BIN: '/usr/bin/chromium' 13 | 14 | before_script: 15 | - apt-get -qq update 16 | - apt-get -qq install libmariadbclient-dev 17 | - apt-get -qq install python-virtualenv python3-virtualenv python-pip python3-pip 18 | - apt-get -qq install chromium chromium-driver 19 | 20 | - curl -sL https://deb.nodesource.com/setup_8.x | bash - 21 | - apt-get -qq install nodejs 22 | - npm install -g jshint jasmine-core karma karma-jasmine karma-chrome-launcher 23 | 24 | test: 25 | script: 26 | - python3 -V 27 | - python2 -V 28 | - npm --version 29 | - chromium --version 30 | - chromedriver --version 31 | - ./tests/test_all.sh 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Patchwork-FDO 2 | ============= 3 | 4 | The missing link between mailing lists and CI. 5 | 6 | Patchwork mainly: 7 | * picks up emails from the mailing list(s) and organizes them in projects and 8 | series 9 | * exports events through an API for each new series (for CI system consumption) 10 | * makes patches/series available as downloadable mboxes 11 | * provides API endpoint for submitting testing results 12 | * sends those results as a reply to the patch/series on the mailing list 13 | 14 | And secondly: 15 | * provides a Web UI to browse, search and download the patches 16 | * give means of tracing patches state, from initial submission to acceptance 17 | 18 | It **supplements** mailing lists, not replaces them. 19 | 20 | How To? 21 | ------- 22 | 23 | Check out `docs/` directory. You can find more details and installation guide 24 | there. 25 | 26 | This Is A Fork 27 | -------------- 28 | 29 | FDO flavor of Patchwork was forked quite a while ago due to the original 30 | project stagnancy. Since then, the original one picked up on development 31 | speed, going in its own direction. You can check it out here: 32 | . 33 | 34 | Links 35 | ----- 36 | 37 | Official Patchwork-FDO repository: 38 | 39 | Freedesktop instance: 40 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /docs/NEWS: -------------------------------------------------------------------------------- 1 | == Cron script changes == 2 | 3 | The patchwork cron script has been moved to a manage.py command. Instead 4 | of running patchwork-cron.py, run: 5 | 6 | ./manage.py cron 7 | 8 | == Upgrading to 3b8a61c == 9 | 10 | Recent commits have changed a few admin-visible components of patchwork, so 11 | upgrading to post-commit 3b8a61c involves a few steps: 12 | 13 | - Update the database schema, by running the 015-add-patch-tags.sql script, 14 | and re-run the grants script. 15 | 16 | For example, on postgres: 17 | 18 | psql -f lib/sql/migration/015-add-patch-tags.sql patchwork 19 | psql -f lib/sql/grant-all.postgres.sql patchwork 20 | 21 | - Update to the new settings infrastructure. By default, settings are read 22 | from patchwork/settings/production.py. To migrate, use the template: 23 | 24 | cp patchwork/settings/production{.example,}.py 25 | 26 | and merge your previous settings (from apps/local_settings.py) into 27 | this file 28 | 29 | - Fixup external references to apps/ 30 | 31 | The apps/ directory is gone; the patchwork module is now in the top-level 32 | directory. If you have scripts that run anything from apps/ (eg, incoming 33 | mail parsers that call parsemail.sh, and cron scripts), then remove the apps/ 34 | directory from those. 35 | 36 | apps/patchwork/ -> patchwork/ 37 | 38 | (or you can create a symlink - apps/ -> .) 39 | 40 | - Update any external scripts to use the new settings module 41 | 42 | If you have been running scripts (eg, from cron) that set the 43 | DJANGO_SETTINGS_MODULE environment variable, you'll need to update that 44 | to the new settings system. Typically: 45 | 46 | DJANGO_SETTINGS_MODULE=patchwork.settings.production 47 | 48 | The manage.py script has been moved from apps/ into the top-level directory 49 | too. 50 | 51 | - Run the 'collectstatic' management command: 52 | 53 | ./manage.py collectstatic 54 | 55 | Ensure that the STATIC_ROOT setting points somewhere sensible (eg, the 56 | absolute path of htdocs/static in the patchwork tree). 57 | 58 | - Update apache to use the new static content. 59 | 60 | Static content is now in all under STATIC_ROOT, the apache configuration 61 | should be simpler now. The core config will be: 62 | 63 | DocumentRoot /srv/patchwork/htdocs/ 64 | Alias /static/ /srv/patchwork/htdocs/static/ 65 | WSGIScriptAlias / /srv/pathchwork/lib/apache2/patchwork.wsgi 66 | WSGIPassAuthorization On 67 | 68 | -------------------------------------------------------------------------------- /docs/TODO: -------------------------------------------------------------------------------- 1 | * Better footer stripping. need to leave 'Update: messages' but remove sigs 2 | * Person link should go to -> (person,project) patch list view 3 | * https for logins 4 | * Patch flags 5 | * Help: XML RPC interface, bundling, Hint headers 6 | * Allow manual update of commit ref 7 | * store rejected mails 8 | * In-message From: header 9 | * Per-user default filter settings 10 | * pwclient: -b to specify multiple patch ids 11 | * pwclient: add bundle manipulation and retrieval ops 12 | * pwclient: specify multiple patches by ID range 13 | * pwclient: integrate hash parser 14 | * pwclient: add example scripts (eg, git post-commit hook) to online help 15 | * pwclient: case-insensitive searches for author and project name 16 | * changing primary email addresses for accounts 17 | -------------------------------------------------------------------------------- /docs/examples/testing-show-series.py: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | import fileinput 3 | import json 4 | 5 | for line in fileinput.input(): 6 | event = json.loads(line) 7 | series = event['series'] 8 | revision = event['parameters']['revision'] 9 | print("series %d (rev %d)" % (series, revision)) 10 | -------------------------------------------------------------------------------- /docs/images/testing-ci-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/docs/images/testing-ci-flow.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Patchwork documentation master file, created by 2 | sphinx-quickstart on Tue Sep 29 17:43:28 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Patchwork's documentation! 7 | ===================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | intro 15 | installation 16 | manual 17 | testing 18 | rest 19 | development 20 | 21 | -------------------------------------------------------------------------------- /docs/intro.rst: -------------------------------------------------------------------------------- 1 | patchwork 2 | ========= 3 | 4 | patchwork is a patch tracking system for community-based projects. It is 5 | intended to make the patch management process easier for both the 6 | project's contributors and maintainers, leaving time for the more 7 | important (and more interesting) stuff. 8 | 9 | Patches that have been sent to a mailing list are 'caught' by the 10 | system, and appear on a web page. Any comments posted that reference the 11 | patch are appended to the patch page too. The project's maintainer can 12 | then scan through the list of patches, marking each with a certain 13 | state, such as Accepted, Rejected or Under Review. Old patches can be 14 | sent to the archive or deleted. 15 | 16 | Currently, patchwork is being used for a number of open-source projects, 17 | mostly subsystems of the Linux kernel. Although Patchwork has been 18 | developed with the kernel workflow in mind, the aim is to be flexible 19 | enough to suit the majority of community projects. 20 | 21 | Download 22 | -------- 23 | 24 | The latest version of Patchwork is available with git. To download: 25 | 26 | :: 27 | 28 | $ git clone https://gitlab.freedesktop.org/patchwork-fdo/patchwork-fdo.git 29 | 30 | Patchwork is distributed under the `GNU General Public 31 | License `__. 32 | 33 | Design 34 | ------ 35 | 36 | patchwork should supplement mailing lists, not replace them 37 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 38 | 39 | Patchwork isn't intended to replace a community mailing list; that's why 40 | you can't comment on a patch in patchwork. If this were the case, then 41 | there would be two forums of discussion on patches, which fragments the 42 | patch review process. Developers who don't use patchwork would get left 43 | out of the discussion. 44 | 45 | However, a future development item for patchwork is to facilitate 46 | on-list commenting, by providing a "send a reply to the list" feature 47 | for logged-in users. 48 | 49 | Don't pollute the project's changelogs with patchwork poop 50 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 51 | 52 | A project's changelogs are valuable - we don't want to add 53 | patchwork-specific metadata. 54 | 55 | patchwork users shouldn't require a specific version control system 56 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 57 | 58 | Not everyone uses git for kernel development, and not everyone uses git 59 | for patchwork-tracked projects. 60 | 61 | It's still possible to hook other programs into patchwork, using the 62 | pwclient command-line client for patchwork, or directly to the XML RPC 63 | interface. 64 | 65 | Getting Started 66 | --------------- 67 | 68 | You should check out the :ref:`installation` and :ref:`development` 69 | guides for information on how to get to work with patchwork. 70 | 71 | Support 72 | ------- 73 | 74 | For questions and contributions, please use the `GitLab project 75 | `__. 76 | -------------------------------------------------------------------------------- /docs/requirements-base.txt: -------------------------------------------------------------------------------- 1 | python-dateutil>2.0,<3.0 2 | djangorestframework>=3.6,<3.7 3 | drf-nested-routers 4 | enum34 5 | jsonfield 6 | django-filter>=1.1.0,<1.2.0 7 | celery>=4.0,<5.0 8 | redis 9 | -------------------------------------------------------------------------------- /docs/requirements-dev-mysql.txt: -------------------------------------------------------------------------------- 1 | django>=1.11,<1.12 2 | mysqlclient>=1.3.0,<1.4 3 | -r requirements-dev.txt 4 | -------------------------------------------------------------------------------- /docs/requirements-dev-postgresql.txt: -------------------------------------------------------------------------------- 1 | django>=1.11,<1.12 2 | psycopg2>=2.7,<2.8 3 | -r requirements-dev.txt 4 | -------------------------------------------------------------------------------- /docs/requirements-dev-sqlite.txt: -------------------------------------------------------------------------------- 1 | django>=1.11,<1.12 2 | -r requirements-dev.txt 3 | -------------------------------------------------------------------------------- /docs/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements-base.txt 2 | django-debug-toolbar==1.9 3 | selenium 4 | sphinx 5 | sphinxcontrib-httpdomain 6 | -------------------------------------------------------------------------------- /docs/requirements-prod-mysql.txt: -------------------------------------------------------------------------------- 1 | django>=1.11,<1.12 2 | mysqlclient>=1.3.0,<1.4 3 | -r requirements-base.txt 4 | -------------------------------------------------------------------------------- /docs/requirements-prod-postgres.txt: -------------------------------------------------------------------------------- 1 | django>=1.11,<1.12 2 | psycopg2>=2.6,<2.7 3 | -r requirements-base.txt 4 | -------------------------------------------------------------------------------- /docs/symbols: -------------------------------------------------------------------------------- 1 | .. |CI| replace:: :abbr:`CI (Continuous Integration)` 2 | .. |UI| replace:: :abbr:`UI (User Interface)` 3 | .. |git| replace:: :command:`git` 4 | .. |git-pw| replace:: :command:`git-pw` 5 | .. |git pw poll-events| replace:: :command:`git pw poll-events` 6 | .. |git pw post-result| replace:: :command:`git pw post-result` 7 | .. |git send-email| replace:: :command:`git send-email` 8 | .. |pip| replace:: :command:`pip` 9 | 10 | .. _diff: https://en.wikipedia.org/wiki/Diff_utility 11 | .. _repository: https://gitlab.freedesktop.org/patchwork-fdo/patchwork-fdo/ 12 | .. _requests: http://docs.python-requests.org/en/master/ 13 | -------------------------------------------------------------------------------- /git-pw/requirements.txt: -------------------------------------------------------------------------------- 1 | GitPython 2 | requests 3 | -------------------------------------------------------------------------------- /htdocs/css/jquery.dynatable.css: -------------------------------------------------------------------------------- 1 | ../../lib/packages/jquery/jquery.dynatable.css -------------------------------------------------------------------------------- /htdocs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/htdocs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /htdocs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/htdocs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /htdocs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/htdocs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /htdocs/images/16-em-cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/htdocs/images/16-em-cross.png -------------------------------------------------------------------------------- /htdocs/images/16-em-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/htdocs/images/16-em-down.png -------------------------------------------------------------------------------- /htdocs/js/bundle.js: -------------------------------------------------------------------------------- 1 | 2 | var editing_order = false; 3 | var dragging = false; 4 | 5 | function order_button_click(node) 6 | { 7 | var rows, form; 8 | 9 | form = $("#reorderform"); 10 | rows = $("#patchlist").get(0).tBodies[0].rows; 11 | 12 | if (rows.length < 1) 13 | return; 14 | 15 | if (editing_order) { 16 | 17 | /* disable the save button */ 18 | node.disabled = true; 19 | 20 | /* add input elements as the sequence of patches */ 21 | for (var i = 0; i < rows.length; i++) { 22 | form.append(''); 24 | } 25 | 26 | form.get(0).submit(); 27 | } else { 28 | 29 | /* store the first order value */ 30 | start_order = row_to_patch_id(rows[0]); 31 | $("input[name='order_start']").attr("value", start_order); 32 | 33 | /* update buttons */ 34 | node.setAttribute("value", "Save order"); 35 | $("#reorder\\-cancel").css("display", "inline"); 36 | 37 | /* show help text */ 38 | $("#reorderhelp").text('Drag & drop rows to reorder'); 39 | 40 | /* enable drag & drop on the patches list */ 41 | $("#patchlist").tableDnD({ 42 | onDragClass: 'dragging', 43 | onDragStart: function() { dragging = true; }, 44 | onDrop: function() { dragging = false; } 45 | }); 46 | 47 | /* replace zebra striping with hover */ 48 | $("#patchlist tbody tr").css("background", "inherit"); 49 | $("#patchlist tbody tr").hover(drag_hover_in, drag_hover_out); 50 | } 51 | 52 | editing_order = !editing_order; 53 | } 54 | 55 | function order_cancel_click(node) 56 | { 57 | node.form.submit(); 58 | } 59 | 60 | /* dragging helper functions */ 61 | function drag_hover_in() 62 | { 63 | if (!dragging) 64 | $(this).addClass("draghover"); 65 | } 66 | function drag_hover_out() 67 | { 68 | $(this).removeClass("draghover"); 69 | } 70 | 71 | function row_to_patch_id(node) 72 | { 73 | var id_str, i; 74 | 75 | id_str = node.getAttribute("id"); 76 | 77 | i = id_str.indexOf(':'); 78 | if (i == -1) 79 | return null; 80 | 81 | return id_str.substring(i + 1); 82 | } 83 | -------------------------------------------------------------------------------- /htdocs/js/common.js: -------------------------------------------------------------------------------- 1 | 2 | function confirm_delete(type, name) 3 | { 4 | return confirm("Are you sure you want to delete the " + type + 5 | " '" + name + "'?"); 6 | } 7 | 8 | function select_all(obj) 9 | { 10 | var value = obj.checked; 11 | var form = obj.form; 12 | 13 | select_all_checkbox = obj; 14 | 15 | for (var i = 0; i < form.elements.length; i++ ) { 16 | var element = form.elements[i]; 17 | if (element.type != 'checkbox') { 18 | continue; 19 | } 20 | if (element.name.substring(0, 9) != 'patch_id:') { 21 | continue; 22 | } 23 | element.checked = value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /htdocs/js/jquery-1.10.1.min.js: -------------------------------------------------------------------------------- 1 | ../../lib/packages/jquery/jquery-1.10.1.min.js -------------------------------------------------------------------------------- /htdocs/js/jquery.dynatable.js: -------------------------------------------------------------------------------- 1 | ../../lib/packages/jquery/jquery.dynatable.js -------------------------------------------------------------------------------- /htdocs/js/jquery.stickytableheaders.min.js: -------------------------------------------------------------------------------- 1 | ../../lib/packages/jquery/jquery.stickytableheaders.min.js -------------------------------------------------------------------------------- /htdocs/js/jquery.tablednd.js: -------------------------------------------------------------------------------- 1 | ../../lib/packages/jquery/jquery.tablednd.js -------------------------------------------------------------------------------- /lib/apache2/patchwork.wsgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Apache2 WSGI handler for patchwork 5 | # 6 | # Copyright © 2010 martin f. krafft 7 | # Released under the GNU General Public License v2 or later. 8 | # 9 | import os 10 | import sys 11 | 12 | basedir = os.path.join( 13 | os.path.dirname(__file__), os.path.pardir, os.path.pardir) 14 | sys.path.append(basedir) 15 | 16 | os.environ['DJANGO_SETTINGS_MODULE'] = 'patchwork.settings.production' 17 | 18 | from django.core.wsgi import get_wsgi_application 19 | application = get_wsgi_application() 20 | -------------------------------------------------------------------------------- /lib/apache2/patchwork.wsgi.conf: -------------------------------------------------------------------------------- 1 | Alias /static/ /srv/patchwork/htdocs/static/ 2 | 3 | Order allow,deny 4 | Allow from all 5 | 6 | 7 | WSGIScriptAlias / /srv/patchwork/lib/apache2/patchwork.wsgi 8 | WSGIPassAuthorization On 9 | -------------------------------------------------------------------------------- /lib/nginx/patchwork.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 4; 3 | pid /var/run/nginx.pid; 4 | 5 | events { 6 | worker_connections 768; 7 | } 8 | 9 | http { 10 | tcp_nopush on; 11 | tcp_nodelay on; 12 | keepalive_timeout 65; 13 | types_hash_max_size 2048; 14 | 15 | gzip on; 16 | gzip_proxied any; 17 | gzip_types text/plain text/css text/javascript application/x-javascript 18 | text/xml application/xml image/svg+xml 19 | application/vnd.ms-fontobject application/x-font-ttf font/opentype; 20 | 21 | include /etc/nginx/mime.types; 22 | default_type application/octet-stream; 23 | 24 | access_log /var/log/nginx/access.log; 25 | error_log /var/log/nginx/error.log; 26 | 27 | server { 28 | location = favicon.ico { access_log off; log_not_found off; } 29 | 30 | location /static { 31 | alias /var/www/patchwork; 32 | expires 3h; 33 | } 34 | 35 | location / { 36 | include uwsgi_params; 37 | uwsgi_pass unix:/run/uwsgi/patchwork.sock; 38 | uwsgi_modifier1 30; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/packages/.gitignore: -------------------------------------------------------------------------------- 1 | django 2 | -------------------------------------------------------------------------------- /lib/packages/jquery/README: -------------------------------------------------------------------------------- 1 | This directory contains the jQuery Javascript library, and jQuery plugins: 2 | 3 | jQuery: 4 | 5 | - http://jquery.com/ 6 | - MIT license 7 | 8 | Table Drag & Drop plugin: 9 | 10 | - http://isocra.com/2008/02/table-drag-and-drop-jquery-plugin/ 11 | - MIT license 12 | 13 | Sticky Table Headers: 14 | 15 | - https://github.com/jmosbech/StickyTableHeaders 16 | - MIT license 17 | 18 | Dynatable: 19 | 20 | - https://github.com/alfajango/jquery-dynatable 21 | - GNU Affero General Public v3 22 | -------------------------------------------------------------------------------- /lib/packages/jquery/jquery.dynatable.css: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Dynatable plugin 0.3.1 3 | * 4 | * Copyright (c) 2014 Steve Schwartz (JangoSteve) 5 | * 6 | * Dual licensed under the AGPL and Proprietary licenses: 7 | * http://www.dynatable.com/license/ 8 | * 9 | * Date: Tue Jan 02 2014 10 | */ 11 | 12 | 13 | .dynatable-search { 14 | float: right; 15 | margin-bottom: 10px; 16 | } 17 | 18 | .dynatable-pagination-links { 19 | float: right; 20 | } 21 | 22 | .dynatable-record-count { 23 | display: block; 24 | padding: 5px 0; 25 | } 26 | 27 | .dynatable-pagination-links span, 28 | .dynatable-pagination-links li { 29 | display: inline-block; 30 | } 31 | 32 | .dynatable-page-link, 33 | .dynatable-page-break { 34 | display: block; 35 | padding: 6px 12px; 36 | } 37 | 38 | .dynatable-page-link { 39 | cursor: pointer; 40 | } 41 | 42 | .dynatable-active-page, 43 | .dynatable-disabled-page { 44 | cursor: text; 45 | } 46 | .dynatable-active-page:hover, 47 | .dynatable-disabled-page:hover { 48 | text-decoration: none; 49 | } 50 | 51 | .dynatable-active-page { 52 | background-color: #eee; 53 | border-radius: 4px; 54 | color: #999; 55 | font-weight:normal; 56 | } 57 | .dynatable-active-page:hover { 58 | color: #999; 59 | } 60 | .dynatable-disabled-page:hover { 61 | background: none; 62 | } 63 | -------------------------------------------------------------------------------- /lib/packages/jquery/jquery.tablednd.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/lib/packages/jquery/jquery.tablednd.js -------------------------------------------------------------------------------- /lib/sql/grant-all.mysql.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | -- give necessary permissions to the web server. Because the admin is all 3 | -- web-based, these need to be quite permissive 4 | GRANT SELECT, UPDATE, INSERT, DELETE ON django_session TO 'www-data'@localhost; 5 | GRANT SELECT, UPDATE, INSERT, DELETE ON django_site TO 'www-data'@localhost; 6 | GRANT SELECT, UPDATE, INSERT, DELETE ON django_admin_log TO 'www-data'@localhost; 7 | GRANT SELECT, UPDATE, INSERT, DELETE ON django_content_type TO 'www-data'@localhost; 8 | GRANT SELECT, UPDATE, INSERT, DELETE ON auth_group_permissions TO 'www-data'@localhost; 9 | GRANT SELECT, UPDATE, INSERT, DELETE ON auth_user TO 'www-data'@localhost; 10 | GRANT SELECT, UPDATE, INSERT, DELETE ON auth_user_groups TO 'www-data'@localhost; 11 | GRANT SELECT, UPDATE, INSERT, DELETE ON auth_group TO 'www-data'@localhost; 12 | GRANT SELECT, UPDATE, INSERT, DELETE ON auth_user_user_permissions TO 'www-data'@localhost; 13 | GRANT SELECT, UPDATE, INSERT, DELETE ON auth_permission TO 'www-data'@localhost; 14 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_emailconfirmation TO 'www-data'@localhost; 15 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_state TO 'www-data'@localhost; 16 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_comment TO 'www-data'@localhost; 17 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_person TO 'www-data'@localhost; 18 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_userprofile TO 'www-data'@localhost; 19 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_userprofile_maintainer_projects TO 'www-data'@localhost; 20 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_project TO 'www-data'@localhost; 21 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_bundle TO 'www-data'@localhost; 22 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_bundlepatch TO 'www-data'@localhost; 23 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_patch TO 'www-data'@localhost; 24 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_emailoptout TO 'www-data'@localhost; 25 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_patchchangenotification TO 'www-data'@localhost; 26 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_tag TO 'www-data'@localhost; 27 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_patchtag TO 'www-data'@localhost; 28 | GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_delegationrule TO 'www-data'@localhost; 29 | 30 | -- allow the mail user (in this case, 'nobody') to add patches 31 | GRANT INSERT, SELECT ON patchwork_patch TO 'nobody'@localhost; 32 | GRANT INSERT, SELECT ON patchwork_comment TO 'nobody'@localhost; 33 | GRANT INSERT, SELECT ON patchwork_person TO 'nobody'@localhost; 34 | GRANT INSERT, SELECT, UPDATE, DELETE ON patchwork_patchtag TO 'nobody'@localhost; 35 | GRANT SELECT ON patchwork_project TO 'nobody'@localhost; 36 | GRANT SELECT ON patchwork_state TO 'nobody'@localhost; 37 | GRANT SELECT ON patchwork_tag TO 'nobody'@localhost; 38 | GRANT SELECT ON patchwork_delegationrule TO 'nobody'@localhost; 39 | 40 | COMMIT; 41 | 42 | -------------------------------------------------------------------------------- /lib/sql/grant-all.postgres.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | -- give necessary permissions to the web server. Because the admin is all 3 | -- web-based, these need to be quite permissive 4 | GRANT SELECT, UPDATE, INSERT, DELETE ON 5 | django_session, 6 | django_site, 7 | django_admin_log, 8 | django_content_type, 9 | auth_group_permissions, 10 | auth_user, 11 | auth_user_groups, 12 | auth_group, 13 | auth_user_user_permissions, 14 | auth_permission, 15 | patchwork_emailconfirmation, 16 | patchwork_state, 17 | patchwork_comment, 18 | patchwork_person, 19 | patchwork_userprofile, 20 | patchwork_userprofile_maintainer_projects, 21 | patchwork_project, 22 | patchwork_bundle, 23 | patchwork_bundlepatch, 24 | patchwork_patch, 25 | patchwork_emailoptout, 26 | patchwork_patchchangenotification, 27 | patchwork_tag, 28 | patchwork_patchtag, 29 | patchwork_delegationrule 30 | TO "www-data"; 31 | GRANT SELECT, UPDATE ON 32 | auth_group_id_seq, 33 | auth_group_permissions_id_seq, 34 | auth_permission_id_seq, 35 | auth_user_groups_id_seq, 36 | auth_user_id_seq, 37 | auth_user_user_permissions_id_seq, 38 | django_admin_log_id_seq, 39 | django_content_type_id_seq, 40 | django_site_id_seq, 41 | patchwork_bundle_id_seq, 42 | patchwork_bundlepatch_id_seq, 43 | patchwork_comment_id_seq, 44 | patchwork_patch_id_seq, 45 | patchwork_person_id_seq, 46 | patchwork_project_id_seq, 47 | patchwork_state_id_seq, 48 | patchwork_emailconfirmation_id_seq, 49 | patchwork_userprofile_id_seq, 50 | patchwork_userprofile_maintainer_projects_id_seq, 51 | patchwork_tag_id_seq, 52 | patchwork_patchtag_id_seq, 53 | patchwork_delegationrule_id_seq 54 | TO "www-data"; 55 | 56 | -- allow the mail user (in this case, 'nobody') to add patches 57 | GRANT INSERT, SELECT ON 58 | patchwork_patch, 59 | patchwork_comment, 60 | patchwork_person 61 | TO "nobody"; 62 | GRANT INSERT, SELECT, UPDATE, DELETE ON 63 | patchwork_patchtag 64 | TO "nobody"; 65 | GRANT SELECT ON 66 | patchwork_project, 67 | patchwork_state, 68 | patchwork_tag, 69 | patchwork_delegationrule 70 | TO "nobody"; 71 | GRANT UPDATE, SELECT ON 72 | patchwork_patch_id_seq, 73 | patchwork_person_id_seq, 74 | patchwork_comment_id_seq, 75 | patchwork_patchtag_id_seq 76 | TO "nobody"; 77 | 78 | COMMIT; 79 | 80 | -------------------------------------------------------------------------------- /lib/sql/migration/001-hex-hash-types.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | ALTER TABLE patchwork_patch ALTER COLUMN hash DROP NOT NULL; 3 | UPDATE patchwork_patch SET hash = NULL; 4 | COMMIT; 5 | BEGIN; 6 | ALTER TABLE patchwork_patch ALTER COLUMN hash TYPE CHAR(40); 7 | CREATE INDEX "patchwork_patch_hash" ON "patchwork_patch" ("hash"); 8 | COMMIT; 9 | -------------------------------------------------------------------------------- /lib/sql/migration/002-extend-userpersonconfirmation-key-length.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | ALTER TABLE patchwork_userpersonconfirmation 3 | ALTER COLUMN key TYPE char(40); 4 | COMMIT; 5 | -------------------------------------------------------------------------------- /lib/sql/migration/003-add-comment-parent.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | ALTER TABLE "patchwork_comment" ADD COLUMN "parent_id" integer NULL; 3 | ALTER TABLE "patchwork_comment" ADD CONSTRAINT parent_id_refs_id_7b721867 4 | FOREIGN KEY ("parent_id") 5 | REFERENCES "patchwork_comment" ("id") 6 | DEFERRABLE INITIALLY DEFERRED; 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /lib/sql/migration/004-msgid-uniqueness.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | ALTER TABLE patchwork_patch DROP CONSTRAINT "patchwork_patch_msgid_key"; 3 | ALTER TABLE patchwork_comment DROP CONSTRAINT "patchwork_comment_msgid_key"; 4 | 5 | ALTER TABLE patchwork_patch ADD UNIQUE ("msgid", "project_id"); 6 | ALTER TABLE patchwork_comment ADD UNIQUE ("msgid", "patch_id"); 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /lib/sql/migration/005-bundle-patch-ordering.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | CREATE TABLE "patchwork_bundlepatch" ( 3 | "id" SERIAL NOT NULL PRIMARY KEY, 4 | "patch_id" INTEGER NOT NULL 5 | REFERENCES "patchwork_patch" ("id") DEFERRABLE INITIALLY DEFERRED, 6 | "bundle_id" INTEGER NOT NULL 7 | REFERENCES "patchwork_bundle" ("id") DEFERRABLE INITIALLY DEFERRED, 8 | "order" SERIAL NOT NULL, 9 | UNIQUE ("bundle_id", "patch_id") 10 | ); 11 | 12 | -- we 'INSERT INTO ... SELECT' (rather than renaming and adding the order 13 | -- column) here so that we can order by date 14 | INSERT INTO patchwork_bundlepatch (id, patch_id, bundle_id) 15 | SELECT patchwork_bundle_patches.id, patch_id, bundle_id 16 | FROM patchwork_bundle_patches 17 | INNER JOIN patchwork_patch 18 | ON patchwork_patch.id = patchwork_bundle_patches.patch_id 19 | ORDER BY bundle_id, patchwork_patch.date; 20 | COMMIT; 21 | 22 | BEGIN; 23 | ALTER TABLE patchwork_bundlepatch 24 | ALTER COLUMN "order" TYPE INTEGER; 25 | 26 | -- initialise the starting number for this sequence 27 | SELECT setval('patchwork_bundlepatch_id_seq', 28 | (SELECT max(id) + 1 FROM patchwork_bundlepatch)); 29 | 30 | DROP TABLE patchwork_bundle_patches; 31 | 32 | -- normalise ordering: order should start with 1 in each bundle 33 | UPDATE patchwork_bundlepatch SET "order" = 1 + "order" - 34 | (SELECT min("order") FROM patchwork_bundlepatch AS p2 35 | WHERE p2.bundle_id = patchwork_bundlepatch.bundle_id); 36 | 37 | GRANT SELECT, INSERT, UPDATE, DELETE ON patchwork_bundlepatch TO "www-data"; 38 | GRANT SELECT, UPDATE ON patchwork_bundlepatch_id_seq TO "www-data"; 39 | 40 | COMMIT; 41 | -------------------------------------------------------------------------------- /lib/sql/migration/007-patch-pull-requests.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | ALTER TABLE patchwork_patch ADD column pull_url varchar(255); 3 | ALTER TABLE patchwork_patch ALTER COLUMN content DROP NOT NULL; 4 | ALTER TABLE patchwork_patch ADD CONSTRAINT has_content_or_url 5 | CHECK (pull_url IS NOT NULL OR content IS NOT NULL); 6 | COMMIT; 7 | -------------------------------------------------------------------------------- /lib/sql/migration/008-confirmations.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | ALTER TABLE "patchwork_userpersonconfirmation" 3 | RENAME TO "patchwork_emailconfirmation"; 4 | ALTER SEQUENCE "patchwork_userpersonconfirmation_id_seq" 5 | RENAME TO "patchwork_emailconfirmation_id_seq"; 6 | ALTER TABLE "patchwork_emailconfirmation" 7 | ALTER COLUMN "user_id" DROP NOT NULL, 8 | ADD COLUMN "type" varchar(20) NOT NULL DEFAULT 'userperson'; 9 | ALTER TABLE "patchwork_emailconfirmation" 10 | ALTER COLUMN "type" DROP DEFAULT; 11 | COMMIT; 12 | -------------------------------------------------------------------------------- /lib/sql/migration/009-drop-registrationprofile.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | DELETE FROM registration_registrationprofile; 4 | 5 | -- unlink users who have contributed 6 | 7 | UPDATE patchwork_person SET user_id = NULL 8 | WHERE user_id IN (SELECT id FROM auth_user WHERE is_active = False) 9 | AND id IN (SELECT DISTINCT submitter_id FROM patchwork_comment); 10 | 11 | -- remove persons who only have a user linkage 12 | 13 | DELETE FROM patchwork_person WHERE user_id IN 14 | (SELECT id FROM auth_user WHERE is_active = False); 15 | 16 | -- delete profiles 17 | 18 | DELETE FROM patchwork_userprofile WHERE user_id IN 19 | (SELECT id FROM auth_user WHERE is_active = False); 20 | 21 | -- delete inactive users 22 | 23 | DELETE FROM auth_user WHERE is_active = False; 24 | 25 | DROP TABLE registration_registrationprofile; 26 | 27 | COMMIT; 28 | -------------------------------------------------------------------------------- /lib/sql/migration/010-optout-tables.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | CREATE TABLE "patchwork_emailoptout" ( 3 | "email" varchar(200) NOT NULL PRIMARY KEY 4 | ); 5 | COMMIT; 6 | -------------------------------------------------------------------------------- /lib/sql/migration/011-patch-change-notifications.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | CREATE TABLE "patchwork_patchchangenotification" ( 3 | "patch_id" integer NOT NULL PRIMARY KEY REFERENCES "patchwork_patch" ("id") DEFERRABLE INITIALLY DEFERRED, 4 | "last_modified" timestamp with time zone NOT NULL, 5 | "orig_state_id" integer NOT NULL REFERENCES "patchwork_state" ("id") DEFERRABLE INITIALLY DEFERRED 6 | ) 7 | ; 8 | ALTER TABLE "patchwork_project" ADD COLUMN 9 | "send_notifications" boolean NOT NULL DEFAULT False; 10 | ALTER TABLE "patchwork_project" ALTER COLUMN 11 | "send_notifications" DROP DEFAULT; 12 | COMMIT; 13 | -------------------------------------------------------------------------------- /lib/sql/migration/012-project-add-columns.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | ALTER TABLE patchwork_project ADD COLUMN web_url varchar(2000); 3 | ALTER TABLE patchwork_project ADD COLUMN scm_url varchar(2000); 4 | ALTER TABLE patchwork_project ADD COLUMN webscm_url varchar(2000); 5 | COMMIT; 6 | -------------------------------------------------------------------------------- /lib/sql/migration/013-bundle-names.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | UPDATE patchwork_bundle 3 | SET name = replace(name, '/', '-') 4 | WHERE name like '%/%'; 5 | COMMIT; 6 | 7 | -------------------------------------------------------------------------------- /lib/sql/migration/014-cleanup-people.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | DELETE FROM patchwork_person WHERE id NOT IN ( 3 | SELECT submitter_id FROM patchwork_patch 4 | UNION 5 | SELECT submitter_id FROM patchwork_comment) 6 | AND user_id IS NULL; 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /lib/sql/migration/015-add-patch-tags.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | ALTER TABLE patchwork_project ADD COLUMN use_tags boolean default true; 3 | 4 | CREATE TABLE "patchwork_tag" ( 5 | "id" serial NOT NULL PRIMARY KEY, 6 | "name" varchar(20) NOT NULL, 7 | "pattern" varchar(50) NOT NULL, 8 | "abbrev" varchar(2) NOT NULL UNIQUE 9 | ); 10 | 11 | CREATE TABLE "patchwork_patchtag" ( 12 | "id" serial NOT NULL PRIMARY KEY, 13 | "patch_id" integer NOT NULL, 14 | "tag_id" integer NOT NULL REFERENCES "patchwork_tag" ("id"), 15 | "count" integer NOT NULL, 16 | UNIQUE ("patch_id", "tag_id") 17 | ); 18 | 19 | COMMIT; 20 | -------------------------------------------------------------------------------- /lib/supervisor/patchwork-celery-worker.conf: -------------------------------------------------------------------------------- 1 | ; ========================================== 2 | ; celery worker configuration for Patchwork 3 | ; ========================================== 4 | 5 | [program:patchworkcelery] 6 | 7 | ; Set full path to celery program if using virtualenv 8 | command=/path/to/virtualenv/bin/celery -A patchwork worker -c 3 --loglevel=INFO 9 | 10 | ; The directory to your Django project 11 | directory=/srv/patchwork.example.com 12 | 13 | ; If supervisord is run as the root user, switch users to this UNIX user account 14 | ; before doing any processing. 15 | user=patchwork 16 | 17 | ; Supervisor will start as many instances of this program as named by numprocs 18 | numprocs=1 19 | 20 | ; Put process stdout output in this file 21 | stdout_logfile=/var/log/patchwork/celery_worker.log 22 | 23 | ; Put process stderr output in this file 24 | stderr_logfile=/var/log/patchwork/celery_worker.log 25 | 26 | ; If true, this program will start automatically when supervisord is started 27 | autostart=true 28 | 29 | ; May be one of false, unexpected, or true. If false, the process will never 30 | ; be autorestarted. If unexpected, the process will be restart when the program 31 | ; exits with an exit code that is not one of the exit codes associated with this 32 | ; process’ configuration (see exitcodes). If true, the process will be 33 | ; unconditionally restarted when it exits, without regard to its exit code. 34 | autorestart=true 35 | 36 | ; The total number of seconds which the program needs to stay running after 37 | ; a startup to consider the start successful. 38 | startsecs=10 39 | 40 | ; Need to wait for currently executing tasks to finish at shutdown. 41 | ; Increase this if you have very long running tasks. 42 | stopwaitsecs = 600 43 | 44 | ; When resorting to send SIGKILL to the program to terminate it 45 | ; send SIGKILL to its whole process group instead, 46 | ; taking care of its children as well. 47 | killasgroup=true 48 | -------------------------------------------------------------------------------- /lib/uwsgi/patchwork.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | 3 | project = patchwork 4 | base = /opt 5 | user = www-data 6 | group = www-data 7 | 8 | chdir = %(base)/%(project) 9 | pythonpath = %(base)/%(project) 10 | module = %(project).wsgi:application 11 | 12 | master = true 13 | processes = 5 14 | # increase buffer size to avoid "502 bad gateway error" 15 | # "recv() failed (104: Connection reset by peer) while reading response header from upstream" 16 | buffer-size = 16384 17 | 18 | uid = %(user) 19 | gid = %(group) 20 | 21 | daemonize = /var/log/%(project).log 22 | socket = /run/uwsgi/%(project).sock 23 | chmod-socket = 660 24 | vacuum = true 25 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", 7 | "patchwork.settings.production") 8 | 9 | from django.core.management import execute_from_command_line 10 | 11 | execute_from_command_line(sys.argv) 12 | -------------------------------------------------------------------------------- /patchwork/__init__.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2016 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | # This will make sure the app is always imported when 23 | # Django starts so that shared_task will use this app. 24 | from .celery import app as celery_app # noqa 25 | -------------------------------------------------------------------------------- /patchwork/bin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/patchwork/bin/__init__.py -------------------------------------------------------------------------------- /patchwork/bin/bash_completion: -------------------------------------------------------------------------------- 1 | # Autocompletion for bash. 2 | 3 | _pwclient() { 4 | local cur prev words cword split 5 | 6 | if declare -f _init_completion >/dev/null; then 7 | _init_completion -s || return 8 | else 9 | cur=$(_get_cword) 10 | prev=${COMP_WORDS[COMP_CWORD-1]} 11 | fi 12 | 13 | case "${COMP_CWORD}" in 14 | 0|1) return 0;; 15 | esac 16 | 17 | projects="$(sed -r -e '/\[options\]/d;' \ 18 | -e '/^\[(.+)\]$/!d;' \ 19 | -e 's//\1/;' ~/.pwclientrc 2>/dev/null)" 20 | 21 | case "${prev}" in 22 | -p) COMPREPLY=( $(compgen -W "${projects}" -- "${cur}" ) );; 23 | esac 24 | 25 | return 0 26 | } 27 | complete -F _pwclient pwclient 28 | 29 | # vim: ft=sh 30 | -------------------------------------------------------------------------------- /patchwork/bin/parsemail-batch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Patchwork - automated patch tracking system 4 | # Copyright (C) 2008 Jeremy Kerr 5 | # 6 | # This file is part of the Patchwork package. 7 | # 8 | # Patchwork is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # Patchwork is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with Patchwork; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | PATCHWORK_BINDIR=`dirname $0` 23 | 24 | if [ $# -ne 1 ] 25 | then 26 | echo "usage: $0 " >&2 27 | exit 1 28 | fi 29 | 30 | mail_dir="$1" 31 | 32 | echo "dir: $mail_dir" 33 | 34 | if [ ! -d "$mail_dir" ] 35 | then 36 | echo "$mail_dir should be a directory"? >&2 37 | exit 1 38 | fi 39 | 40 | ls -1rt "$mail_dir" | 41 | while read line; 42 | do 43 | echo $line 44 | $PATCHWORK_BINDIR/parsemail.sh < "$mail_dir/$line" 45 | done 46 | -------------------------------------------------------------------------------- /patchwork/bin/parsemail.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Patchwork - automated patch tracking system 4 | # Copyright (C) 2008 Jeremy Kerr 5 | # 6 | # This file is part of the Patchwork package. 7 | # 8 | # Patchwork is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # Patchwork is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with Patchwork; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | BIN_DIR=`dirname $0` 23 | PATCHWORK_BASE=`readlink -e $BIN_DIR/../..` 24 | 25 | PYTHONPATH="$PATCHWORK_BASE":"$PATCHWORK_BASE/lib/python:$PYTHONPATH" \ 26 | DJANGO_SETTINGS_MODULE=patchwork.settings.production \ 27 | "$PATCHWORK_BASE/patchwork/bin/parsemail.py" $@ 28 | 29 | exit 0 30 | -------------------------------------------------------------------------------- /patchwork/bin/update-patchwork-status.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Patchwork - automated patch tracking system 4 | # Copyright (C) 2008 Jeremy Kerr 5 | # 6 | # This file is part of the Patchwork package. 7 | # 8 | # Patchwork is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # Patchwork is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with Patchwork; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | from __future__ import print_function 23 | 24 | from optparse import OptionParser 25 | import subprocess 26 | import sys 27 | 28 | 29 | def commits(options, revlist): 30 | cmd = ['git', 'rev-list', revlist] 31 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd=options.repodir) 32 | 33 | revs = [] 34 | 35 | for line in proc.stdout.readlines(): 36 | revs.append(line.strip()) 37 | 38 | return revs 39 | 40 | 41 | def commit(options, rev): 42 | cmd = ['git', 'diff', '%(rev)s^..%(rev)s' % {'rev': rev}] 43 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd=options.repodir) 44 | 45 | buf = proc.communicate()[0] 46 | 47 | return buf 48 | 49 | 50 | def main(args): 51 | parser = OptionParser(usage='%prog [options] revspec') 52 | parser.add_option("-p", "--project", dest="project", action='store', 53 | help="use project PROJECT", metavar="PROJECT") 54 | parser.add_option("-d", "--dir", dest="repodir", action='store', 55 | help="use git repo in DIR", metavar="DIR") 56 | 57 | (options, args) = parser.parse_args(args[1:]) 58 | 59 | if len(args) != 1: 60 | parser.error("incorrect number of arguments") 61 | 62 | revspec = args[0] 63 | revs = commits(options, revspec) 64 | 65 | for rev in revs: 66 | print(rev) 67 | print(commit(options, rev)) 68 | 69 | 70 | if __name__ == '__main__': 71 | sys.exit(main(sys.argv)) 72 | -------------------------------------------------------------------------------- /patchwork/celery.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2016 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | import os 23 | 24 | from django.conf import settings # noqa 25 | from celery import Celery 26 | 27 | # set the default Django settings module for the 'celery' program. 28 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 29 | 'patchwork.settings.production') 30 | 31 | app = Celery('patchwork') 32 | 33 | # Using a string here means the worker will not have to 34 | # pickle the object when using Windows. 35 | app.config_from_object('django.conf:settings') 36 | app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) 37 | 38 | 39 | @app.task(bind=True) 40 | def debug_task(self): 41 | print('Request: {0!r}'.format(self.request)) 42 | -------------------------------------------------------------------------------- /patchwork/context_processors.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2017 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from django.contrib.sites.models import Site 21 | 22 | from patchwork.models import Bundle 23 | 24 | 25 | def settings(request): 26 | return {'settings': settings} 27 | 28 | 29 | def site(request): 30 | return {'site': Site.objects.get_current()} 31 | 32 | 33 | def bundle(request): 34 | user = request.user 35 | if not user.is_authenticated(): 36 | return {} 37 | return {'bundles': Bundle.objects.filter(owner=user)} 38 | -------------------------------------------------------------------------------- /patchwork/email.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2016 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from django.conf import settings 21 | from django.core import mail 22 | from django.template.loader import render_to_string 23 | 24 | 25 | class NotificationEmail(object): 26 | defaults = { 27 | 'from_email': settings.DEFAULT_FROM_EMAIL, 28 | } 29 | 30 | def send_email(self, ctx, to_email, **kwargs): 31 | if not to_email: 32 | return 33 | 34 | subject_template = ('emails/%s_notification.subject.txt' % 35 | self.base_name) 36 | subject = render_to_string(subject_template, ctx).strip() 37 | body_template = 'emails/%s_notification.body.txt' % self.base_name 38 | body = render_to_string(body_template, ctx) 39 | 40 | email = mail.EmailMessage(subject=subject, body=body, 41 | from_email=settings.DEFAULT_FROM_EMAIL, 42 | to=[to_email], 43 | **kwargs) 44 | email.send() 45 | 46 | 47 | class ReviewerNotification(NotificationEmail): 48 | def __init__(self, series, series_url, user, reviewer): 49 | self.series = series 50 | self.user = user 51 | self.reviewer = reviewer 52 | self.ctx = {'series': series, 53 | 'series_url': series_url, 54 | 'user': user, 55 | 'reviewer': reviewer} 56 | 57 | def send(self): 58 | # do not notify if the reviewer is the same person that has set this 59 | # field 60 | if self.user == self.reviewer: 61 | return 62 | self.send_email(self.ctx, self.reviewer.email) 63 | 64 | 65 | class NewReviewerNotification(ReviewerNotification): 66 | base_name = 'new_reviewer' 67 | 68 | 69 | class PreviousReviewerNotification(ReviewerNotification): 70 | base_name = 'previous_reviewer' 71 | -------------------------------------------------------------------------------- /patchwork/fields.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2008 Jeremy Kerr 3 | # Copyright (C) 2015 Intel Corporation 4 | # 5 | # This file is part of the Patchwork package. 6 | # 7 | # Patchwork is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Patchwork is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Patchwork; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | 21 | from __future__ import absolute_import 22 | 23 | import hashlib 24 | 25 | from django.db import models 26 | from django.utils import six 27 | 28 | 29 | class HashField(models.CharField): 30 | 31 | def __init__(self, *args, **kwargs): 32 | self.n_bytes = len(hashlib.sha1().hexdigest()) 33 | kwargs['max_length'] = self.n_bytes 34 | 35 | super(HashField, self).__init__(*args, **kwargs) 36 | 37 | def construct(self, value): 38 | if isinstance(value, six.text_type): 39 | value = value.encode('utf-8') 40 | return hashlib.sha1(value) 41 | 42 | def from_db_value(self, value, expression, connection, context): 43 | return self.to_python(value) 44 | 45 | def db_type(self, connection=None): 46 | return 'char(%d)' % self.n_bytes 47 | -------------------------------------------------------------------------------- /patchwork/fixtures/default_events.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | series-new-revision 5 | 6 | 7 | patch-state-change 8 | 9 | 10 | pull-request-new 11 | 12 | 13 | -------------------------------------------------------------------------------- /patchwork/fixtures/default_projects.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | cbe-oss-dev 7 | Cell Broadband Engine development 8 | cbe-oss-dev.ozlabs.org 9 | cbe-oss-dev@ozlabs.org 10 | 11 | 12 | linuxppc-dev 13 | Linux PPC development 14 | linuxppc-dev.ozlabs.org 15 | linuxppc-dev@ozlabs.org 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /patchwork/fixtures/default_states.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | New 7 | 0 8 | True 9 | 10 | 11 | Under Review 12 | 1 13 | True 14 | 15 | 16 | Accepted 17 | 2 18 | False 19 | 20 | 21 | Rejected 22 | 3 23 | False 24 | 25 | 26 | RFC 27 | 4 28 | False 29 | 30 | 31 | Not Applicable 32 | 5 33 | False 34 | 35 | 36 | Changes Requested 37 | 6 38 | False 39 | 40 | 41 | Awaiting Upstream 42 | 7 43 | False 44 | 45 | 46 | Superseded 47 | 8 48 | False 49 | 50 | 51 | Deferred 52 | 9 53 | False 54 | 55 | 56 | -------------------------------------------------------------------------------- /patchwork/fixtures/default_tags.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Acked-by 5 | ^Acked-by: 6 | A 7 | 8 | 9 | Reviewed-by 10 | ^Reviewed-by: 11 | R 12 | 13 | 14 | Tested-by 15 | ^Tested-by: 16 | T 17 | 18 | -------------------------------------------------------------------------------- /patchwork/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/patchwork/management/__init__.py -------------------------------------------------------------------------------- /patchwork/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/patchwork/management/commands/__init__.py -------------------------------------------------------------------------------- /patchwork/management/commands/cron.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2015 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from django.core.management.base import BaseCommand 21 | 22 | from patchwork.utils import send_notifications, do_expiry 23 | 24 | 25 | class Command(BaseCommand): 26 | help = ('Run periodic patchwork functions: send notifications and ' 27 | 'expire unused users') 28 | 29 | def handle(self, *args, **kwargs): 30 | errors = send_notifications() 31 | for (recipient, error) in errors: 32 | self.stderr.write("Failed sending to %s: %s" % 33 | (recipient.email, error)) 34 | 35 | do_expiry() 36 | -------------------------------------------------------------------------------- /patchwork/management/commands/rehash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Patchwork - automated patch tracking system 4 | # Copyright (C) 2008 Jeremy Kerr 5 | # Copyright (C) 2015 Intel Corporation 6 | # 7 | # This file is part of the Patchwork package. 8 | # 9 | # Patchwork is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # Patchwork is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with Patchwork; if not, write to the Free Software 21 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 | 23 | from django.core.management.base import BaseCommand 24 | 25 | from patchwork.models import Patch 26 | 27 | 28 | class Command(BaseCommand): 29 | help = 'Update the hashes on existing patches' 30 | args = '[...]' 31 | 32 | def handle(self, *args, **options): 33 | query = Patch.objects 34 | 35 | if args: 36 | query = query.filter(id_in=args) 37 | else: 38 | query = query.all() 39 | 40 | count = query.count() 41 | 42 | for i, patch in enumerate(query.iterator()): 43 | patch.hash = None 44 | patch.save() 45 | if (i % 10) == 0: 46 | self.stdout.write('%06d/%06d\r' % (i, count), ending='') 47 | self.stdout.flush() 48 | self.stdout.write('\ndone') 49 | -------------------------------------------------------------------------------- /patchwork/management/commands/retag.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2015 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | import sys 21 | from django.core.management.base import BaseCommand 22 | 23 | from patchwork.models import Patch 24 | 25 | 26 | class Command(BaseCommand): 27 | help = 'Update the tag (Ack/Review/Test) counts on existing patches' 28 | args = '[...]' 29 | 30 | def handle(self, *args, **options): 31 | query = Patch.objects 32 | 33 | if args: 34 | query = query.filter(id__in=args) 35 | else: 36 | query = query.all() 37 | 38 | count = query.count() 39 | 40 | for i, patch in enumerate(query.iterator()): 41 | patch.refresh_tag_counts() 42 | if (i % 10) == 0: 43 | sys.stdout.write('%06d/%06d\r' % (i, count)) 44 | sys.stdout.flush() 45 | sys.stdout.write('%06d/%06d\r' % (count, count)) 46 | sys.stdout.write('\ndone\n') 47 | -------------------------------------------------------------------------------- /patchwork/management/commands/retriageproject.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2015 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from email.parser import HeaderParser 21 | import sys 22 | 23 | from django.core.management.base import BaseCommand 24 | from patchwork.models import Patch, Project 25 | from patchwork.bin.parsemail import find_project 26 | 27 | 28 | class Command(BaseCommand): 29 | help = 'Reassign project of patches and series when ' \ 30 | 'subject_prefix_tags changes' 31 | args = 'project' 32 | 33 | def handle(self, *args, **options): 34 | 35 | if len(args) != 1: 36 | print('error: Need a project.') 37 | sys.exit(1) 38 | 39 | try: 40 | project = Project.objects.get(linkname=args[0]) 41 | except Project.DoesNotExist: 42 | print("error: can't find project '%s'" % args[0]) 43 | sys.exit(1) 44 | 45 | parser = HeaderParser() 46 | query = Patch.objects.filter(project=project) 47 | count = query.count() 48 | for i, patch in enumerate(query.iterator()): 49 | if (i % 10) == 0: 50 | sys.stdout.write("%06d/%06d\r" % (i, count)) 51 | sys.stdout.flush() 52 | 53 | headers = parser.parsestr(patch.headers) 54 | new_project = find_project(headers) 55 | if new_project == patch.project: 56 | continue 57 | 58 | patch.project = new_project 59 | patch.save() 60 | series = patch.series() 61 | if not series: 62 | continue 63 | series.project = new_project 64 | series.save() 65 | 66 | sys.stdout.write("%06d/%06d\r" % (count, count)) 67 | sys.stdout.write('\ndone\n') 68 | -------------------------------------------------------------------------------- /patchwork/middleware.py: -------------------------------------------------------------------------------- 1 | class AccessControlAllowOriginMiddleware: 2 | """Allow all API GET (read-only) requests from any domain""" 3 | def process_response(self, request, response): 4 | if request.path.startswith('/api/') and \ 5 | (request.method == 'GET' or request.method == 'OPTIONS'): 6 | response['Access-Control-Allow-Origin'] = '*' 7 | response['Access-Control-Allow-Headers'] = 'Content-Type' 8 | return response 9 | -------------------------------------------------------------------------------- /patchwork/migrations/0002_fix_patch_state_default_values.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='patch', 16 | name='state', 17 | field=models.ForeignKey(to='patchwork.State', null=True, on_delete=models.CASCADE), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0003_series.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import datetime 6 | from django.conf import settings 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('patchwork', '0002_fix_patch_state_default_values'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Series', 19 | fields=[ 20 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 21 | ('name', models.CharField(default=b'Series without cover letter', max_length=200)), 22 | ('submitted', models.DateTimeField(default=datetime.datetime.now)), 23 | ('last_updated', models.DateTimeField(auto_now=True)), 24 | ('version', models.IntegerField(default=1)), 25 | ('n_patches', models.IntegerField(default=0)), 26 | ('project', models.ForeignKey(to='patchwork.Project', on_delete=models.CASCADE)), 27 | ('reviewer', models.ForeignKey(related_name='reviewers', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)), 28 | ('submitter', models.ForeignKey(related_name='submitters', to='patchwork.Person', on_delete=models.CASCADE)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name='SeriesRevision', 33 | fields=[ 34 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 35 | ('version', models.IntegerField(default=1)), 36 | ('root_msgid', models.CharField(max_length=255)), 37 | ('cover_letter', models.TextField(null=True, blank=True)), 38 | ], 39 | options={ 40 | 'ordering': ['version'], 41 | }, 42 | ), 43 | migrations.CreateModel( 44 | name='SeriesRevisionPatch', 45 | fields=[ 46 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 47 | ('order', models.IntegerField()), 48 | ('patch', models.ForeignKey(to='patchwork.Patch', on_delete=models.CASCADE)), 49 | ('revision', models.ForeignKey(to='patchwork.SeriesRevision', on_delete=models.CASCADE)), 50 | ], 51 | options={ 52 | 'ordering': ['order'], 53 | }, 54 | ), 55 | migrations.AddField( 56 | model_name='seriesrevision', 57 | name='patches', 58 | field=models.ManyToManyField(to='patchwork.Patch', through='patchwork.SeriesRevisionPatch'), 59 | ), 60 | migrations.AddField( 61 | model_name='seriesrevision', 62 | name='series', 63 | field=models.ForeignKey(to='patchwork.Series', on_delete=models.CASCADE), 64 | ), 65 | migrations.AlterUniqueTogether( 66 | name='seriesrevisionpatch', 67 | unique_together=set([('revision', 'order'), ('revision', 'patch')]), 68 | ), 69 | migrations.AlterUniqueTogether( 70 | name='seriesrevision', 71 | unique_together=set([('series', 'version')]), 72 | ), 73 | ] 74 | -------------------------------------------------------------------------------- /patchwork/migrations/0004_project_git_send_email_only.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0003_series'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='project', 16 | name='git_send_email_only', 17 | field=models.BooleanField(default=False), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0005_event_eventlog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('patchwork', '0004_project_git_send_email_only'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Event', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('name', models.CharField(max_length=20)), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='EventLog', 25 | fields=[ 26 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 27 | ('event_time', models.DateTimeField(auto_now=True)), 28 | ('event', models.ForeignKey(to='patchwork.Event', on_delete=models.CASCADE)), 29 | ('series', models.ForeignKey(to='patchwork.Series', on_delete=models.CASCADE)), 30 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)), 31 | ], 32 | options={ 33 | 'ordering': ['-event_time'], 34 | }, 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /patchwork/migrations/0006_eventlog_parameters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import jsonfield.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('patchwork', '0005_event_eventlog'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='eventlog', 17 | name='parameters', 18 | field=jsonfield.fields.JSONField(null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /patchwork/migrations/0007_multiple_projects_same_ml.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0006_eventlog_parameters'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='project', 16 | name='subject_prefix_tags', 17 | field=models.CharField(help_text=b'Comma separated list of tags', max_length=255, blank=True), 18 | ), 19 | migrations.AlterField( 20 | model_name='project', 21 | name='listid', 22 | field=models.CharField(max_length=255), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /patchwork/migrations/0008_test_results.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | import datetime 6 | from django.conf import settings 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('patchwork', '0007_multiple_projects_same_ml'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Test', 19 | fields=[ 20 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 21 | ('name', models.CharField(max_length=255)), 22 | ('project', models.ForeignKey(to='patchwork.Project', on_delete=models.CASCADE)), 23 | ], 24 | ), 25 | migrations.CreateModel( 26 | name='TestResult', 27 | fields=[ 28 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 29 | ('date', models.DateTimeField(default=datetime.datetime.now)), 30 | ('state', models.SmallIntegerField(choices=[(0, b'pending'), (1, b'success'), (2, b'warning'), (3, b'failure')])), 31 | ('url', models.URLField(null=True, blank=True)), 32 | ('summary', models.TextField(null=True, blank=True)), 33 | ('patch', models.ForeignKey(blank=True, to='patchwork.Patch', null=True, on_delete=models.CASCADE)), 34 | ('revision', models.ForeignKey(blank=True, to='patchwork.SeriesRevision', null=True, on_delete=models.CASCADE)), 35 | ('test', models.ForeignKey(to='patchwork.Test', on_delete=models.CASCADE)), 36 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), 37 | ], 38 | ), 39 | migrations.AlterUniqueTogether( 40 | name='testresult', 41 | unique_together=set([('test', 'patch'), ('test', 'revision')]), 42 | ), 43 | migrations.AlterUniqueTogether( 44 | name='test', 45 | unique_together=set([('project', 'name')]), 46 | ), 47 | ] 48 | -------------------------------------------------------------------------------- /patchwork/migrations/0009_test_results_mail.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0008_test_results'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='test', 16 | name='mail_condition', 17 | field=models.SmallIntegerField(default=0, choices=[(0, b'always'), (1, b'on failure')]), 18 | ), 19 | migrations.AddField( 20 | model_name='test', 21 | name='mail_recipient', 22 | field=models.SmallIntegerField(default=0, choices=[(0, b'none'), (1, b'submitter'), (2, b'mailing list')]), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /patchwork/migrations/0010_test_results_to_cc_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0009_test_results_mail'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='test', 16 | name='mail_cc_list', 17 | field=models.CharField(help_text=b'Comma separated list of emails', max_length=255, null=True, blank=True), 18 | ), 19 | migrations.AddField( 20 | model_name='test', 21 | name='mail_to_list', 22 | field=models.CharField(help_text=b'Comma separated list of emails', max_length=255, null=True, blank=True), 23 | ), 24 | migrations.AlterField( 25 | model_name='test', 26 | name='mail_recipient', 27 | field=models.SmallIntegerField(default=0, choices=[(0, b'none'), (1, b'submitter'), (2, b'mailing list'), (3, b'recipient list')]), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /patchwork/migrations/0011_test_results_updated_ts_fix.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0010_test_results_to_cc_list'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='testresult', 16 | name='date', 17 | field=models.DateTimeField(auto_now=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0012_project_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0011_test_results_updated_ts_fix'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='project', 16 | name='description', 17 | field=models.TextField(null=True, blank=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0013_seriesrevision_test_state.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | def update_revision_test_state(apps, schema_editor): 8 | SeriesRevision = apps.get_model("patchwork", "SeriesRevision") 9 | TestResult = apps.get_model("patchwork", "TestResult") 10 | query = SeriesRevision.objects.all() 11 | 12 | for _, revision in enumerate(query.iterator()): 13 | results = TestResult.objects.filter(revision=revision) 14 | if results.count() > 0: 15 | revision.test_state = max([r.state for r in results]) 16 | revision.save() 17 | 18 | 19 | def noop(apps, schema_editor): 20 | pass 21 | 22 | 23 | class Migration(migrations.Migration): 24 | 25 | dependencies = [ 26 | ('patchwork', '0012_project_description'), 27 | ] 28 | 29 | operations = [ 30 | migrations.AddField( 31 | model_name='seriesrevision', 32 | name='test_state', 33 | field=models.SmallIntegerField(null=True, choices=[(0, b'pending'), (1, b'success'), (2, b'warning'), (3, b'failure')]), 34 | ), 35 | 36 | migrations.RunPython(update_revision_test_state, noop), 37 | ] 38 | -------------------------------------------------------------------------------- /patchwork/migrations/0014_last_revision.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | def update_last_revision_n_patches(apps, schema_editor): 8 | Series = apps.get_model("patchwork", "Series") 9 | SeriesRevision = apps.get_model("patchwork", "SeriesRevision") 10 | 11 | query = Series.objects.all() 12 | for _, series in enumerate(query.iterator()): 13 | revisions = SeriesRevision.objects.filter(series=series). \ 14 | order_by('version').reverse() 15 | 16 | for field in series._meta.local_fields: 17 | if field.name == "last_updated": 18 | field.auto_now = False 19 | 20 | series.last_revision = revisions[0] 21 | series.save() 22 | 23 | for field in series._meta.local_fields: 24 | if field.name == "last_updated": 25 | field.auto_now = True 26 | 27 | for revision in revisions: 28 | revision.n_patches = series.n_patches 29 | revision.save() 30 | 31 | 32 | def noop(apps, schema_editor): 33 | pass 34 | 35 | 36 | class Migration(migrations.Migration): 37 | 38 | dependencies = [ 39 | ('patchwork', '0013_seriesrevision_test_state'), 40 | ] 41 | 42 | operations = [ 43 | migrations.AddField( 44 | model_name='series', 45 | name='last_revision', 46 | field=models.OneToOneField(related_name='+', null=True, to='patchwork.SeriesRevision', on_delete=models.CASCADE), 47 | ), 48 | migrations.AddField( 49 | model_name='seriesrevision', 50 | name='n_patches', 51 | field=models.IntegerField(default=0), 52 | ), 53 | 54 | migrations.RunPython(update_last_revision_n_patches, noop), 55 | ] 56 | -------------------------------------------------------------------------------- /patchwork/migrations/0015_remove_version_n_patches.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | def noop(apps, schema_editor): 8 | pass 9 | 10 | 11 | def update_version_n_patches(apps, schema_editor): 12 | Series = apps.get_model("patchwork", "Series") 13 | SeriesRevision = apps.get_model("patchwork", "SeriesRevision") 14 | 15 | query = Series.objects.all() 16 | for _, series in enumerate(query.iterator()): 17 | revisions = SeriesRevision.objects.filter(series=series). \ 18 | order_by('version').reverse() 19 | last_revision = revisions[0] 20 | 21 | for field in series._meta.local_fields: 22 | if field.name == "last_updated": 23 | field.auto_now = False 24 | 25 | series.version = last_revision.version 26 | series.n_patches = last_revision.n_patches 27 | series.save() 28 | 29 | for field in series._meta.local_fields: 30 | if field.name == "last_updated": 31 | field.auto_now = True 32 | 33 | 34 | class Migration(migrations.Migration): 35 | 36 | dependencies = [ 37 | ('patchwork', '0014_last_revision'), 38 | ] 39 | 40 | operations = [ 41 | migrations.RunPython(noop, update_version_n_patches), 42 | 43 | migrations.RemoveField( 44 | model_name='series', 45 | name='n_patches', 46 | ), 47 | migrations.RemoveField( 48 | model_name='series', 49 | name='version', 50 | ), 51 | ] 52 | -------------------------------------------------------------------------------- /patchwork/migrations/0016_add_delegation_rule_model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('patchwork', '0015_remove_version_n_patches'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='DelegationRule', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('path', models.CharField(max_length=255)), 21 | ('priority', models.IntegerField(default=0)), 22 | ('project', models.ForeignKey(to='patchwork.Project', on_delete=models.CASCADE)), 23 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), 24 | ], 25 | options={ 26 | 'ordering': ['-priority', 'path'], 27 | }, 28 | ), 29 | migrations.AlterUniqueTogether( 30 | name='delegationrule', 31 | unique_together=set([('path', 'project')]), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /patchwork/migrations/0017_series_meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0016_add_delegation_rule_model'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='series', 16 | options={'verbose_name_plural': 'Series'}, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /patchwork/migrations/0018_test_email_on_error.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0017_series_meta'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='test', 16 | name='mail_condition', 17 | field=models.SmallIntegerField(default=0, choices=[(0, b'always'), (1, b'on warning/failure'), (2, b'on failure')]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0019_eventlog_patch.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0018_test_email_on_error'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='eventlog', 16 | name='patch', 17 | field=models.ForeignKey(to='patchwork.Patch', null=True, on_delete=models.CASCADE), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0020_series_state.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | import jsonfield.fields 6 | 7 | from patchwork.models import _revision_update_state 8 | 9 | 10 | def noop(apps, schema_editor): 11 | pass 12 | 13 | 14 | def update_revision_state(apps, schema_editor): 15 | SeriesRevision = apps.get_model("patchwork", "SeriesRevision") 16 | 17 | query = SeriesRevision.objects.all() 18 | for _, revision in enumerate(query.iterator()): 19 | _revision_update_state(revision) 20 | 21 | 22 | class Migration(migrations.Migration): 23 | 24 | dependencies = [ 25 | ('patchwork', '0019_eventlog_patch'), 26 | ] 27 | 28 | operations = [ 29 | migrations.AddField( 30 | model_name='seriesrevision', 31 | name='state', 32 | field=models.SmallIntegerField(default=0, choices=[(0, b'incomplete'), (1, b'initial'), (2, b'in progress'), (3, b'done')]), 33 | ), 34 | migrations.AddField( 35 | model_name='seriesrevision', 36 | name='state_summary', 37 | field=jsonfield.fields.JSONField(null=True), 38 | ), 39 | 40 | migrations.RunPython(update_revision_state, noop), 41 | ] 42 | -------------------------------------------------------------------------------- /patchwork/migrations/0021_unselectable_maintainer_projects.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0020_series_state'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='userprofile', 16 | name='maintainer_projects', 17 | field=models.ManyToManyField(related_name='maintainer_project', to='patchwork.Project', blank=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0022_add_test_result_state_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | def refresh_revision_test_state(apps, revision): 8 | TestResult = apps.get_model("patchwork", "TestResult") 9 | 10 | results = TestResult.objects.filter(revision=revision) 11 | if results.count() > 0: 12 | revision.test_state = max([r.state for r in results]) 13 | else: 14 | revision.test_state = None 15 | 16 | revision.save() 17 | 18 | 19 | def update_revision_state(apps, schema_editor): 20 | SeriesRevision = apps.get_model("patchwork", "SeriesRevision") 21 | 22 | query = SeriesRevision.objects.all() 23 | for _, revision in enumerate(query.iterator()): 24 | refresh_revision_test_state(apps, revision) 25 | 26 | 27 | # We've inserted an 'info' state with a value of 1, so bump all non 0 test 28 | # results by 1. 29 | def migrate_results(apps, schema_editor): 30 | TestResult = apps.get_model("patchwork", "TestResult") 31 | 32 | query = TestResult.objects.all() 33 | for _, result in enumerate(query.iterator()): 34 | if result.state > 0: 35 | result.state += 1 36 | result.save() 37 | 38 | update_revision_state(apps, schema_editor) 39 | 40 | 41 | # drop 'info' results and restore previous state values 42 | def unmigrate_results(apps, schema_editor): 43 | TestResult = apps.get_model("patchwork", "TestResult") 44 | 45 | query = TestResult.objects.all() 46 | for _, result in enumerate(query.iterator()): 47 | if result.state == 1: 48 | result.delete() 49 | continue 50 | 51 | if result.state > 1: 52 | result.state -= 1 53 | result.save() 54 | 55 | update_revision_state(apps, schema_editor) 56 | 57 | 58 | class Migration(migrations.Migration): 59 | 60 | dependencies = [ 61 | ('patchwork', '0021_unselectable_maintainer_projects'), 62 | ] 63 | 64 | operations = [ 65 | migrations.RunPython(migrate_results, unmigrate_results), 66 | 67 | migrations.AlterField( 68 | model_name='seriesrevision', 69 | name='test_state', 70 | field=models.SmallIntegerField(null=True, choices=[(0, b'pending'), (2, b'success'), (3, b'warning'), (4, b'failure'), (1, b'info')]), 71 | ), 72 | migrations.AlterField( 73 | model_name='testresult', 74 | name='state', 75 | field=models.SmallIntegerField(choices=[(0, b'pending'), (2, b'success'), (3, b'warning'), (4, b'failure'), (1, b'info')]), 76 | ), 77 | ] 78 | -------------------------------------------------------------------------------- /patchwork/migrations/0023_fix_test_state_order.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0022_add_test_result_state_info'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='seriesrevision', 16 | name='test_state', 17 | field=models.SmallIntegerField(null=True, choices=[(0, b'pending'), (1, b'info'), (2, b'success'), (3, b'warning'), (4, b'failure')]), 18 | ), 19 | migrations.AlterField( 20 | model_name='testresult', 21 | name='state', 22 | field=models.SmallIntegerField(choices=[(0, b'pending'), (1, b'info'), (2, b'success'), (3, b'warning'), (4, b'failure')]), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /patchwork/migrations/0024_blank_test_state.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0023_fix_test_state_order'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='seriesrevision', 16 | name='test_state', 17 | field=models.SmallIntegerField(blank=True, null=True, choices=[(0, b'pending'), (1, b'info'), (2, b'success'), (3, b'warning'), (4, b'failure')]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0025_patch_last_updated.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | def set_auto_now(obj, field_name, enable): 8 | for field in obj._meta.local_fields: 9 | if field.name == field_name: 10 | field.auto_now = enable 11 | 12 | 13 | def noop(apps, schema_editor): 14 | pass 15 | 16 | 17 | def set_patch_last_updated(apps, schema_editor): 18 | Patch = apps.get_model("patchwork", "Patch") 19 | 20 | query = Patch.objects.all() 21 | for _, patch in enumerate(query.iterator()): 22 | patch.last_updated = patch.date 23 | set_auto_now(patch, "last_updated", False) 24 | patch.save() 25 | set_auto_now(patch, "last_updated", True) 26 | 27 | 28 | class Migration(migrations.Migration): 29 | 30 | dependencies = [ 31 | ('patchwork', '0024_blank_test_state'), 32 | ] 33 | 34 | operations = [ 35 | migrations.AddField( 36 | model_name='patch', 37 | name='last_updated', 38 | field=models.DateTimeField(auto_now=True, null=True), 39 | ), 40 | 41 | migrations.RunPython(set_patch_last_updated, noop), 42 | 43 | migrations.AlterField( 44 | model_name='patch', 45 | name='last_updated', 46 | field=models.DateTimeField(auto_now=True), 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /patchwork/migrations/0026_event_series_not_mandatory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('patchwork', '0025_patch_last_updated'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='EventLog', 16 | name='series', 17 | field=models.ForeignKey(to='patchwork.Series', null=True, on_delete=models.CASCADE), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0027_auto_20180116_0044.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-01-16 00:44 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('patchwork', '0026_event_series_not_mandatory'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='series', 17 | options={'ordering': ['-id'], 'verbose_name_plural': 'Series'}, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /patchwork/migrations/0029_seriesrevision_is_rerun.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.16 on 2018-11-27 20:31 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('patchwork', '0028_auto_20180220_2246'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='seriesrevision', 17 | name='is_rerun', 18 | field=models.BooleanField(default=False), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /patchwork/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/patchwork/migrations/__init__.py -------------------------------------------------------------------------------- /patchwork/permissions.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2018 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from patchwork.models import Project, Patch, Series 21 | 22 | 23 | class Can: 24 | def __init__(self, user): 25 | self.user = user 26 | 27 | def edit(self, obj): 28 | if not self.user.is_authenticated(): 29 | return False 30 | 31 | can = self 32 | if isinstance(obj, Project): 33 | project = obj 34 | return (self.user.is_authenticated and 35 | project in self.user.profile.maintainer_projects.all()) 36 | 37 | if isinstance(obj, Patch): 38 | patch = obj 39 | return (self.user.is_authenticated and 40 | (patch.submitter.user == self.user or 41 | can.edit(patch.project))) 42 | 43 | return False 44 | 45 | def retest(self, obj): 46 | can = self 47 | if isinstance(obj, Series): 48 | series = obj 49 | return (can.edit(series.project) or 50 | series.submitter.user == self.user) 51 | 52 | return False 53 | -------------------------------------------------------------------------------- /patchwork/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/patchwork/settings/__init__.py -------------------------------------------------------------------------------- /patchwork/settings/dev-sqlite.py: -------------------------------------------------------------------------------- 1 | """ 2 | Quick development settings for patchwork project. 3 | 4 | Most of these are commented out as they will be installation dependent. 5 | 6 | Design based on: 7 | http://www.revsys.com/blog/2014/nov/21/recommended-django-project-layout/ 8 | """ 9 | 10 | from __future__ import absolute_import 11 | 12 | from .base import * # noqa 13 | from os.path import join, dirname 14 | 15 | # 16 | # Core settings 17 | # https://docs.djangoproject.com/en/1.6/ref/settings/#core-settings 18 | # 19 | 20 | # Security 21 | # 22 | # You'll need to replace this to a random string. The following python code can 23 | # be used to generate a secret key: 24 | # 25 | # import string, random 26 | # chars = string.letters + string.digits + string.punctuation 27 | # print repr("".join([random.choice(chars) for i in range(0,50)])) 28 | 29 | SECRET_KEY = '00000000000000000000000000000000000000000000000000' 30 | 31 | # Database 32 | # 33 | # SQLite database is used for development. 34 | # Please see https://docs.djangoproject.com/en/1.7/ref/settings/#databases 35 | # for documentation about database configuration. 36 | 37 | DATABASES = { 38 | 'default': { 39 | 'ENGINE': 'django.db.backends.sqlite3', 40 | 'NAME': join(dirname(__file__), 'db.sqlite'), 41 | }, 42 | } 43 | 44 | DEBUG = True 45 | TEMPLATE_DEBUG = True 46 | ENABLE_XMLRPC = True 47 | -------------------------------------------------------------------------------- /patchwork/settings/dev.py: -------------------------------------------------------------------------------- 1 | """ 2 | Development settings for patchwork project. 3 | 4 | These are also used in unit tests. 5 | 6 | Design based on: 7 | http://www.revsys.com/blog/2014/nov/21/recommended-django-project-layout/ 8 | """ 9 | 10 | from __future__ import absolute_import 11 | from .base import * # noqa 12 | 13 | # 14 | # Core settings 15 | # https://docs.djangoproject.com/en/1.6/ref/settings/#core-settings 16 | # 17 | 18 | # Security 19 | 20 | SECRET_KEY = '00000000000000000000000000000000000000000000000000' 21 | 22 | # Debugging 23 | 24 | DEBUG = True 25 | 26 | # Database 27 | 28 | DATABASES = { 29 | 'default': { 30 | 'ENGINE': 'django.db.backends.mysql', 31 | 'HOST': os.getenv('PW_TEST_DB_HOST', 'localhost'), 32 | 'PORT': '', 33 | 'USER': os.getenv('PW_TEST_DB_USER', 'patchwork'), 34 | 'PASSWORD': os.getenv('PW_TEST_DB_PASS', 'password'), 35 | 'NAME': os.getenv('PW_TEST_DB_NAME', 'patchwork'), 36 | }, 37 | } 38 | 39 | if os.getenv('PW_TEST_DB_TYPE', None) == 'postgres': 40 | DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql_psycopg2' 41 | 42 | DATABASES['default']['TEST'] = { 43 | 'CHARSET': 'utf8', 44 | } 45 | 46 | # Email 47 | 48 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 49 | 50 | # 51 | # Third-party application settings 52 | # 53 | 54 | # django-debug-toolbar 55 | INSTALLED_APPS += [ 56 | 'debug_toolbar' 57 | ] 58 | 59 | DEBUG_TOOLBAR_PATCH_SETTINGS = False 60 | 61 | # This should go first in the middleware classes 62 | MIDDLEWARE_CLASSES = [ 63 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 64 | ] + MIDDLEWARE_CLASSES 65 | 66 | INTERNAL_IPS = ['127.0.0.1', '::1'] 67 | 68 | 69 | # 70 | # Patchwork settings 71 | # 72 | 73 | ENABLE_XMLRPC = True 74 | -------------------------------------------------------------------------------- /patchwork/settings/production.example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sample production-ready settings for patchwork project. 3 | 4 | Most of these are commented out as they will be installation dependent. 5 | 6 | Design based on: 7 | http://www.revsys.com/blog/2014/nov/21/recommended-django-project-layout/ 8 | """ 9 | 10 | from __future__ import absolute_import 11 | 12 | import os 13 | 14 | from .base import * # noqa 15 | 16 | # 17 | # Core settings 18 | # https://docs.djangoproject.com/en/1.6/ref/settings/#core-settings 19 | # 20 | 21 | # Security 22 | # 23 | # You'll need to replace this to a random string. The following python code can 24 | # be used to generate a secret key: 25 | # 26 | # import string, random 27 | # chars = string.letters + string.digits + string.punctuation 28 | # print repr("".join([random.choice(chars) for i in range(0,50)])) 29 | 30 | SECRET_KEY = os.environ['DJANGO_SECRET_KEY'] 31 | 32 | # Email 33 | # 34 | # Replace this with your own details 35 | 36 | EMAIL_HOST = os.getenv('EMAIL_HOST', 'localhost') 37 | EMAIL_PORT = os.getenv('EMAIL_PORT', 25) 38 | EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '') 39 | EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '') 40 | EMAIL_USE_TLS = True 41 | 42 | DEFAULT_FROM_EMAIL = 'Patchwork ' 43 | SERVER_EMAIL = DEFAULT_FROM_EMAIL 44 | NOTIFICATION_FROM_EMAIL = DEFAULT_FROM_EMAIL 45 | 46 | ADMINS = ( 47 | ('Jeremy Kerr', 'jk@ozlabs.org'), 48 | ) 49 | 50 | # Database 51 | # 52 | # If you're using a postgres database, connecting over a local unix-domain 53 | # socket, then the following setting should work for you. Otherwise, 54 | # see https://docs.djangoproject.com/en/1.7/ref/settings/#databases 55 | 56 | DATABASES = { 57 | 'default': { 58 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 59 | 'NAME': os.environ.get('DATABASE_NAME', ''), 60 | 'USER': os.environ.get('DATABASE_USER', ''), 61 | 'PASSWORD': os.environ.get('DATABASE_PASSWORD', ''), 62 | 'HOST': os.environ.get('DATABASE_HOST', ''), 63 | 'PORT': os.environ.get('DATABASE_PORT', ''), 64 | }, 65 | } 66 | 67 | # 68 | # Static files settings 69 | # https://docs.djangoproject.com/en/1.7/ref/settings/#static-files 70 | # https://docs.djangoproject.com/en/1.8/ref/contrib/staticfiles/#manifeststaticfilesstorage 71 | # 72 | 73 | STATIC_ROOT = os.environ.get('STATIC_ROOT', '/srv/patchwork/htdocs/static') 74 | STATICFILES_STORAGE = \ 75 | 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' 76 | -------------------------------------------------------------------------------- /patchwork/tasks.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2016 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | from celery import task 23 | from celery.utils.log import get_task_logger 24 | from django.contrib.auth.models import User 25 | 26 | from patchwork.email import (PreviousReviewerNotification, 27 | NewReviewerNotification) 28 | from patchwork.models import Series 29 | 30 | logger = get_task_logger(__name__) 31 | 32 | 33 | @task(name="send_reviewer_notification") 34 | def send_reviewer_notification(series_pk, series_url, user_pk, 35 | old_reviewer_pk, new_reviewer_pk): 36 | series = Series.objects.get(pk=series_pk) 37 | user = User.objects.get(pk=user_pk) 38 | 39 | logger.info("Sending reviewer notification(s) for series %d" % series_pk) 40 | 41 | if old_reviewer_pk is not None: 42 | old_reviewer = User.objects.get(pk=old_reviewer_pk) 43 | email = PreviousReviewerNotification(series, series_url, user, 44 | old_reviewer) 45 | email.send() 46 | if new_reviewer_pk is not None: 47 | new_reviewer = User.objects.get(pk=new_reviewer_pk) 48 | email = NewReviewerNotification(series, series_url, user, new_reviewer) 49 | email.send() 50 | -------------------------------------------------------------------------------- /patchwork/templates/emails/new_reviewer_notification.body.txt: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | {{user.name}} has requested a review from you for: 4 | 5 | {{series.human_name}} 6 | {{series_url}} 7 | 8 | Please review this series at your earliest convience or speak with the 9 | requester if short on time. 10 | 11 | Thanks, 12 | 13 | -- 14 | Sent by Patchwork 15 | -------------------------------------------------------------------------------- /patchwork/templates/emails/new_reviewer_notification.subject.txt: -------------------------------------------------------------------------------- 1 | Review request for {{series.human_name}} 2 | -------------------------------------------------------------------------------- /patchwork/templates/emails/previous_reviewer_notification.body.txt: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | You are no longer the reviewer for: 4 | 5 | {{series.human_name}} 6 | {{series_url}} 7 | 8 | Until next time! 9 | 10 | -- 11 | Sent by Patchwork 12 | -------------------------------------------------------------------------------- /patchwork/templates/emails/previous_reviewer_notification.subject.txt: -------------------------------------------------------------------------------- 1 | Review request dropped for {{series.human_name}} 2 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/activation_email.txt: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | This email is to confirm your account on the patchwork patch-tracking 4 | system. You can activate your account by visiting the url: 5 | 6 | http://{{site.domain}}{% url 'confirm' key=confirmation.key %} 7 | 8 | If you didn't request a user account on patchwork, then you can ignore 9 | this mail. 10 | 11 | Happy patchworking. 12 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/activation_email_subject.txt: -------------------------------------------------------------------------------- 1 | Patchwork account confirmation 2 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/bundle.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load person %} 4 | {% load static %} 5 | 6 | {% block headers %} 7 | 8 | 9 | {% endblock %} 10 | {% block title %}{{project.name}}{% endblock %} 11 | 12 | {% block body %} 13 | 14 |

This bundle contains patches for the {{ bundle.project.linkname }} 15 | project.

16 | 17 |

Download bundle as mbox

18 | 19 | {% if bundleform %} 20 |
21 | {% csrf_token %} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {{ bundleform }} 30 | 31 | 35 | 36 |
Bundle settings
32 | 33 | 34 |
37 |
38 | 39 |
40 | {% endif %} 41 | 42 | {% include "patchwork/patch-list.html" %} 43 | 44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/bundles.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load static %} 4 | 5 | {% block title %}Bundles{% endblock %} 6 | {% block bundle_active %}active{% endblock %} 7 | 8 | {% block body %} 9 |

Bundles

10 | 11 | {% if bundles %} 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | {% for bundle in bundles %} 22 | 23 | 24 | 25 | 30 | 31 | 35 | 45 | 46 | 47 | {% endfor %} 48 |
NameProjectPublic LinkPatches 18 | DownloadDelete
{{ bundle.name }}{{ bundle.project.linkname }} 26 | {% if bundle.public %} 27 | {{ bundle.public_url }} 28 | {% endif %} 29 | {{ bundle.n_patches }}download 36 |
38 | {% csrf_token %} 39 | {{ bundle.delete_form.as_p }} 40 | 43 |
44 |
49 | {% endif %} 50 | 51 |

Bundles are groups of related patches. You can create bundles by 52 | selecting patches from a project, then using the 'create bundle' form 53 | to give your bundle a name. Each bundle can be public or private; public 54 | bundles are given a persistent URL, based you your username and the name 55 | of the bundle. Private bundles are only visible to you.

56 | 57 | {% if not bundles %} 58 |

You have no bundles.

59 | {% endif %} 60 | {% endblock %} 61 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/confirm-error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Confirmation{% endblock %} 4 | {% block heading %}Confirmation{% endblock %} 5 | 6 | 7 | {% block body %} 8 | 9 | {% if error == 'inactive' %} 10 |

This confirmation has already been processed; you've probably visited this 11 | page before.

12 | {% endif %} 13 | 14 | {% if error == 'expired' %} 15 |

The confirmation has expired. If you'd still like to perform the 16 | {{conf.get_type_display}} process, you'll need to resubmit the request.

17 | {% endif %} 18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/filters.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 27 | 28 |
29 |
30 | Show patches with: 31 | {% if filters.applied_filters %} 32 | {% for filter in filters.applied_filters %} 33 | {{ filter.name }} = {{ filter.condition }} 34 | {% if not filter.forced %} 35 |    38 | {% endif %} 39 | {% if not forloop.last %}   |   {% endif %} 40 | {% endfor %} 41 | {% else %} 42 | none   45 | {% endif %} 46 |
47 | 64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/help/about.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}About{% endblock %} 4 | {% block heading %} - About Patchwork{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

Patchwork is free software, and is available from the 9 | Patchwork website.

10 | 11 |

Patchwork is built on the django 12 | web framework.

13 | 14 |

Icons from the Sweetie icon set. 15 | 16 | {% endblock %} 17 | 18 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/help/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/help/pwclient.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Command-line client{% endblock %} 4 | {% block heading %} - Command-line client{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

pwclient is the command-line client for Patchwork. Currently, 9 | it provides access to some read-only features of Patchwork, such as downloading 10 | and applying patches.

11 | 12 |

To use pwclient, you will need:

13 |
    14 |
  • The pwclient 15 | program (11kB, python script)
  • 16 |
  • (optional) a .pwclientrc file in your home directory.
  • 17 |
18 | 19 |

You can create your own .pwclientrc file. Each 20 | Patchwork project 21 | provides a sample linked from the 'project info' page.

22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/list.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load person %} 4 | {% load static %} 5 | 6 | {% block title %}{{project.name}}{% endblock %} 7 | {% block patch_active %}active{% endblock %} 8 | 9 | {% block body %} 10 | 11 |

Patches

12 | 13 | {% if errors %} 14 |

The following error{{ errors|length|pluralize:" was,s were" }} encountered 15 | while updating patches:

16 |
    17 | {% for error in errors %} 18 |
  • {{ error }}
  • 19 | {% endfor %} 20 |
21 | {% endif %} 22 | 23 | {% include "patchwork/patch-list.html" %} 24 | 25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Login{% endblock %} 4 | {% block heading %}Login{% endblock %} 5 | 6 | {% block headers %} 7 | 12 | {% endblock %} 13 | 14 | {% block body %} 15 |
16 | {% csrf_token %} 17 | 18 | 19 | 20 | 21 | {% if error %} 22 | 23 | 24 | 25 | {% endif %} 26 | {{ form }} 27 | 28 | 31 | 36 | 37 |
login
{{ error }}
29 | 30 | 32 | 33 | Forgot password? 34 | 35 |
38 |
39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/mail-form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}mail settings{% endblock %} 4 | {% block heading %}mail settings{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

You can configure Patchwork to send you mail on certain events, 9 | or block automated mail altogether. Enter your email address to 10 | view or change your email settings.

11 | 12 |
13 | {% csrf_token %} 14 | 15 | {% if form.errors %} 16 | 17 | 20 | 21 | {% endif %} 22 | 23 | 24 | 28 | 29 | 30 | 33 | 34 |
18 | There was an error accessing your mail settings: 19 |
{{ form.email.label_tag }} 25 | {{form.email}} 26 | {{form.email.errors}} 27 |
31 | 32 |
35 |
36 | 37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/mail-settings.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}mail settings{% endblock %} 4 | {% block heading %}mail settings{% endblock %} 5 | 6 | {% block body %} 7 |

Settings for {{email}}:

8 | 9 | 10 | 11 | 12 | {% if is_optout %} 13 | 15 | 22 | 23 | {% else %} 24 | 26 | 33 | {% endif %} 34 | 35 |
Opt-out listPatchwork may not send automated notifications to 14 | this address. 16 |
17 | {% csrf_token %} 18 | 19 | 20 |
21 |
Patchwork may send automated notifications to 25 | this address. 27 |
28 | {% csrf_token %} 29 | 30 | 31 |
32 |
36 | 37 | {% endblock %} 38 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/optin-request.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}opt-in{% endblock %} 4 | {% block heading %}opt-in{% endblock %} 5 | 6 | {% block body %} 7 | {% if email_sent %} 8 |

Opt-in confirmation email sent

9 |

An opt-in confirmation mail has been sent to 10 | {{confirmation.email}}, containing a link. Please click on 11 | that link to confirm your opt-in.

12 | {% else %} 13 | {% if error %} 14 |

{{error}}

15 | {% endif %} 16 | 17 | {% if form %} 18 |

This form allows you to opt-in to automated email from Patchwork. Use 19 | this if you have previously opted-out of Patchwork mail, but now want to 20 | received notifications from Patchwork.

21 | When you submit it, an email will be sent to your address with a link to click 22 | to finalise the opt-in. Patchwork does this to prevent someone opting you in 23 | without your consent.

24 |
25 | {% csrf_token %} 26 | {{form.email.errors}} 27 |
28 | {{form.email.label_tag}}: {{form.email}} 29 |
30 | 31 |
32 | {% endif %} 33 | 34 | {% if error and admins %} 35 |

If you are having trouble opting in, please email 36 | {% for admin in admins %} 37 | {% if admins|length > 1 and forloop.last %} or {% endif %} 38 | {{admin.0}} <{{admin.1}}>{% if admins|length > 2 and not forloop.last %}, {% endif %} 40 | {% endfor %} 41 | {% endif %} 42 | 43 | {% endif %} 44 | 45 | {% if user.is_authenticated %} 46 |

Return to your user 47 | profile.

48 | {% endif %} 49 | 50 | {% endblock %} 51 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/optin-request.mail: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | This email is to confirm that you would like to opt-in to automated 4 | email from the Patchwork system at {{site.domain}}. 5 | 6 | To complete the opt-in process, visit: 7 | 8 | http://{{site.domain}}{% url 'confirm' key=confirmation.key %} 9 | 10 | If you didn't request this opt-in, you don't need to do anything. 11 | 12 | Happy patchworking. 13 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/optin.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}opt-in{% endblock %} 4 | {% block heading %}opt-in{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

Opt-in complete. You have successfully opted back in to 9 | automated email from this Patchwork system, using the address 10 | {{email}}.

11 |

If you later decide that you no longer want to receive automated mail from 12 | Patchwork, just visit http://{{site.domain}}{% url 'mail_settings' %}, or 14 | visit the main Patchwork page and navigate from there.

15 | {% if user.is_authenticated %} 16 |

Return to your user 17 | profile.

18 | {% endif %} 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/optout-request.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}opt-out{% endblock %} 4 | {% block heading %}opt-out{% endblock %} 5 | 6 | {% block body %} 7 | {% if email_sent %} 8 |

Opt-out confirmation email sent

9 |

An opt-out confirmation mail has been sent to 10 | {{confirmation.email}}, containing a link. Please click on 11 | that link to confirm your opt-out.

12 | {% else %} 13 | {% if error %} 14 |

{{error}}

15 | {% endif %} 16 | 17 | {% if form %} 18 |

This form allows you to opt-out of automated email from Patchwork.

19 |

If you opt-out of email, Patchwork may still email you if you do certain 20 | actions yourself (such as create a new Patchwork account), but will not send 21 | you unsolicited email.

22 | When you submit it, one email will be sent to your address with a link to click 23 | to finalise the opt-out. Patchwork does this to prevent someone opting you out 24 | without your consent.

25 |
26 | {% csrf_token %} 27 | {{form.email.errors}} 28 |
29 | {{form.email.label_tag}}: {{form.email}} 30 |
31 | 32 |
33 | {% endif %} 34 | 35 | {% if error and admins %} 36 |

If you are having trouble opting out, please email 37 | {% for admin in admins %} 38 | {% if admins|length > 1 and forloop.last %} or {% endif %} 39 | {{admin.0}} <{{admin.1}}>{% if admins|length > 2 and not forloop.last %}, {% endif %} 41 | {% endfor %} 42 | {% endif %} 43 | 44 | {% endif %} 45 | 46 | {% if user.is_authenticated %} 47 |

Return to your user 48 | profile.

49 | {% endif %} 50 | 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/optout-request.mail: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | This email is to confirm that you would like to opt-out from all email 4 | from the Patchwork system at {{site.domain}}. 5 | 6 | To complete the opt-out process, visit: 7 | 8 | http://{{site.domain}}{% url 'confirm' key=confirmation.key %} 9 | 10 | If you didn't request this opt-out, you don't need to do anything. 11 | 12 | Happy patchworking. 13 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/optout.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}opt-out{% endblock %} 4 | {% block heading %}opt-out{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

Opt-out complete. You have successfully opted-out of 9 | automated notifications from this Patchwork system, from the address 10 | {{email}}

11 |

Please note that you may still receive email from other Patchwork setups at 12 | different sites, as they are run independently. You may need to opt-out of 13 | those separately.

14 |

If you later decide to receive mail from Patchwork, just visit 15 | http://{{site.domain}}{% url 'mail_settings' %}, or 17 | visit the main Patchwork page and navigate from there.

18 | {% if user.is_authenticated %} 19 |

Return to your user 20 | profile.

21 | {% endif %} 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/pagination.html: -------------------------------------------------------------------------------- 1 | {% load listurl %} 2 | 3 | {% ifnotequal page.paginator.num_pages 1 %} 4 |
5 | {% if page.has_previous %} 6 | 7 | « 9 | {% else %} 10 | « 11 | {% endif %} 12 | 13 | {% if page.paginator.trailing_set %} 14 | {% for p in page.paginator.trailing_set %} 15 | {{ p }} 16 | {% endfor %} 17 | ... 18 | {% endif %} 19 | 20 | {% for p in page.paginator.adjacent_set %} 21 | {% ifequal p page.number %} 22 | {{ p }} 23 | {% else %} 24 | {{ p }} 26 | {% endifequal %} 27 | {% endfor %} 28 | 29 | {% if page.paginator.leading_set %} 30 | … 31 | {% for p in page.paginator.leading_set %} 32 | {{ p }} 33 | {% endfor %} 34 | {% endif %} 35 | 36 | {% if page.has_next %} 37 | 38 | » 40 | 41 | {% else %} 42 | » 43 | {% endif %} 44 |
45 | {% endifnotequal %} 46 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/patch-change-notification-subject.text: -------------------------------------------------------------------------------- 1 | [{{ projects|join:"," }}] Patch notification: {{notifications|length}} patch{{notifications|length|pluralize:"es"}} updated 2 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/patch-change-notification.mail: -------------------------------------------------------------------------------- 1 | Hello, 2 | 3 | The following patch{{notifications|length|pluralize:"es"}} (submitted by you) {{notifications|length|pluralize:"has,have"}} been updated in Patchwork: 4 | {% for notification in notifications %} 5 | * {{notification.patch.project.linkname}}: {{notification.patch.name|safe}} 6 | - http://{{site.domain}}{{notification.patch.get_absolute_url}} 7 | - for: {{notification.patch.project.name}} 8 | was: {{notification.orig_state}} 9 | now: {{notification.patch.state}} 10 | {% endfor %} 11 | This email is a notification only - you do not need to respond. 12 | 13 | Happy patchworking. 14 | 15 | -- 16 | 17 | This is an automated mail sent by the Patchwork system at 18 | {{site.domain}}. To stop receiving these notifications, edit 19 | your mail settings at: 20 | http://{{site.domain}}{% url 'mail_settings' %} 21 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/project.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ project.name }}{% endblock %} 4 | {% block info_active %}active{% endblock %} 5 | 6 | {% block body %} 7 |

About {{project.name}}

8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {% if project.web_url %} 37 | 38 | 39 | 40 | 41 | {% endif %} 42 | {% if project.webscm_url %} 43 | 44 | 45 | 46 | 47 | {% endif %} 48 | {% if project.scm_url %} 49 | 50 | 51 | 52 | 53 | {% endif %} 54 |
Name{{project.name}} 13 |
List address{{project.listemail}}
Maintainer{{maintainers|length|pluralize}} 21 | {% for maintainer in maintainers %} 22 | {{ maintainer.profile.name }} 23 | <{{maintainer.email}}> 24 |
25 | {% endfor %} 26 |
Patches {{n_patches}} (+ {{n_archived_patches}} archived)
Series{{n_series}}
Website{{project.web_url}}
Source Code Web Interface{{project.webscm_url}}
Source Code Manager URL{{project.scm_url}}
55 | 56 | {% if settings.ENABLE_XMLRPC %} 57 |

Sample Patchwork 58 | client configuration for this project: .pwclientrc.

61 | {% endif %} 62 | 63 | {% endblock %} 64 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/projects.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Project List{% endblock %} 4 | {% block navbarmenu %} 5 | 12 | {% endblock %} 13 | {% block body %} 14 | 15 |
16 | 17 | {% if projects %} 18 | {% for p in projects %} 19 | {% cycle '
' '' '' %} 20 |
21 |
22 |
23 |

{{p.name}}

24 |

25 | View series 26 | | 27 | View patches 28 |

29 | {% if p.description %} 30 |

{{p.description}}

31 | {% endif %} 32 | {% if p.web_url %} 33 |

{{p.web_url}}

34 | {% else %} 35 | {% if p.webscm_url %} 36 |

{{p.webscm_url}}

37 | {% endif %} 38 | {% endif %} 39 |
40 |
41 |
42 | {% if forloop.last %} 43 |
44 | {% else %} 45 | {% cycle '' '' '' %} 46 | {% endif %} 47 | {% endfor %} 48 | {% else %} 49 |

Patchwork doesn't have any projects to display!

50 | {% endif %} 51 | 52 | {% endblock %} 53 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/pwclient: -------------------------------------------------------------------------------- 1 | ../../bin/pwclient -------------------------------------------------------------------------------- /patchwork/templates/patchwork/pwclientrc: -------------------------------------------------------------------------------- 1 | # Sample .pwclientrc file for the {{ project.linkname }} project, 2 | # running on {{ site.domain }}. 3 | # 4 | # Just append this file to your existing ~/.pwclientrc 5 | # If you do not already have a ~/.pwclientrc, then copy this file to 6 | # ~/.pwclientrc, and uncomment the following two lines: 7 | # [options] 8 | # default={{ project.linkname }} 9 | 10 | [{{ project.linkname }}] 11 | url= {{scheme}}://{{site.domain}}{% url 'xmlrpc' %} 12 | {% if user.is_authenticated %} 13 | username: {{ user.username }} 14 | password: 15 | {% endif %} 16 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/register.mail: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | This email is to confirm your account on the Patchwork patch-tracking 4 | system. You can activate your account by visiting the url: 5 | 6 | http://{{site.domain}}{% url 'registration_activateactivation_key'=request.key %} 7 | 8 | If you didn't request a user account on Patchwork, then you can ignore 9 | this mail. 10 | 11 | Happy patchworking. 12 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/registration-confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Registration{% endblock %} 4 | {% block heading %}Registration{% endblock %} 5 | 6 | {% block body %} 7 |

Registration confirmed!

8 | 9 |

Your Patchwork registration is complete. Head over to your profile to start using 11 | Patchwork's extra features.

12 | 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/registration_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Registration{% endblock %} 4 | {% block heading %}Registration{% endblock %} 5 | 6 | 7 | {% block body %} 8 | 9 | {% if confirmation and not error %} 10 |

Registration successful!

11 |

A confirmation email has been sent to {{ confirmation.email }}. You'll 12 | need to visit the link provided in that email to confirm your 13 | registration.

14 |

15 | {% else %} 16 |

By creating a Patchwork account, you can:

17 |

    18 |
  • create "bundles" of patches
  • 19 |
  • update the state of your own patches
  • 20 |
21 |
22 | {% csrf_token %} 23 | 24 | 25 | 26 | 27 | {% if error %} 28 | 29 | 30 | 31 | {% endif %} 32 | 33 | 34 | 35 | 44 | 45 | 46 | 47 | 48 | 57 | 58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 68 | 77 | 78 | 79 | 80 | 81 | 84 | 85 | 86 | 87 | 88 | 97 | 98 | 99 | 100 | 101 | 110 | 111 | 112 | 113 | 116 | 117 |
register
{{ error }}
{{ form.first_name.label_tag }} 36 | {% if form.first_name.errors %} 37 | {{ form.first_name.errors }} 38 | {% endif %} 39 | {{ form.first_name }} 40 | {% if form.first_name.help_text %} 41 |
{{ form.first_name.help_text }}
42 | {% endif %} 43 |
{{ form.last_name.label_tag }} 49 | {% if form.last_name.errors %} 50 | {{ form.last_name.errors }} 51 | {% endif %} 52 | {{ form.last_name }} 53 | {% if form.last_name.help_text %} 54 |
{{ form.last_name.help_text }}
55 | {% endif %} 56 |
62 | Your name is used to identify you on the site 63 |
{{ form.email.label_tag }} 69 | {% if form.email.errors %} 70 | {{ form.email.errors }} 71 | {% endif %} 72 | {{ form.email }} 73 | {% if form.email.help_text %} 74 |
{{ form.email.help_text }}
75 | {% endif %} 76 |
82 | Patchwork will send a confirmation email to this address 83 |
{{ form.username.label_tag }} 89 | {% if form.username.errors %} 90 | {{ form.username.errors }} 91 | {% endif %} 92 | {{ form.username }} 93 | {% if form.username.help_text %} 94 |
{{ form.username.help_text }}
95 | {% endif %} 96 |
{{ form.password.label_tag }} 102 | {% if form.password.errors %} 103 | {{ form.password.errors }} 104 | {% endif %} 105 | {{ form.password }} 106 | {% if form.password.help_text %} 107 |
{{ form.password.help_text }}
108 | {% endif %} 109 |
114 | 115 |
118 |
119 | {% endif %} 120 | 121 | {% endblock %} 122 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/series-list-table.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
16 | 17 | IDSeriesTestsStatusVersionPatchesSubmitterReviewerUpdated
32 | 33 |
34 |
35 | 36 |
37 |
38 | 39 |
40 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/series-list-templates.html: -------------------------------------------------------------------------------- 1 | {% verbatim %} 2 | 10 | 14 | {% endverbatim %} 15 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/test-result.html: -------------------------------------------------------------------------------- 1 |
2 | {% if test_result.summary %} 3 | 25 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/todo-list.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load staticfiles %} 4 | {% load person %} 5 | 6 | {% block title %}{{ user }}'s todo list{% endblock %} 7 | 8 | {% block headers_prepatchwork %} 9 | {% include "patchwork/series-list-templates.html" %} 10 | 11 | {% endblock %} 12 | 13 | {% block headers %} 14 | 25 | {% endblock %} 26 | 27 | {% block body %} 28 |

TODO

29 | 30 |

The Patchwork TODO-list contains patches and series that are assigned to you 31 | and need your attention.

32 | 33 | {% if n_series %} 34 |

Series

35 | 36 | {% include "patchwork/series-list-table.html" %} 37 | {% endif %} 38 | 39 | {% if n_patches %} 40 |

Patches

41 | 42 | {% include "patchwork/patch-list.html" %} 43 | {% endif %} 44 | 45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/todo-lists.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ user }}'s todo lists{% endblock %} 4 | {% block heading %}{{ user }}'s todo lists{% endblock %} 5 | 6 | {% block body %} 7 |

TODO

8 | 9 | {% if todo_lists %} 10 |

You have multiple todo lists. Each todo list contains series and patches for 11 | a single project.

12 | 13 | 14 | 15 | 16 | 17 | 18 | {% for todo_list in todo_lists %} 19 | 20 | 23 | 24 | 25 | 26 | {% endfor %} 27 |
ProjectSeriesPatches
{{ todo_list.project.name }}{{ todo_list.n_series }}{{ todo_list.n_patches }}
28 | 29 | {% else %} 30 | No todo lists 31 | {% endif %} 32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/user-link-confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ user.username }}{% endblock %} 4 | {% block heading %}link accounts for {{ user.username }}{% endblock %} 5 | 6 | 7 | {% block body %} 8 | 9 | {% if errors %} 10 |

{{ errors }}

11 | {% else %} 12 |

You have successfully linked the email address {{ person.email }} to 13 | your Patchwork account

14 | 15 | {% endif %} 16 |

Back to your 17 | profile.

18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/user-link.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{ user.username }}{% endblock %} 4 | {% block heading %}link accounts for {{ user.username }}{% endblock %} 5 | 6 | 7 | {% block body %} 8 | 9 | {% if confirmation and not error %} 10 |

A confirmation email has been sent to {{ confirmation.email }}. Click 11 | on the link provided in the email to confirm that this address belongs to 12 | you.

13 | 14 | {% else %} 15 | 16 | {% if form.errors %} 17 |

There was an error submitting your link request.

18 | {{ form.non_field_errors }} 19 | {% endif %} 20 | {% if error %} 21 |
  • {{error}}
22 | {% endif %} 23 | 24 |
25 | {% csrf_token %} 26 | {{linkform.email.errors}} 27 | Link an email address: {{ linkform.email }} 28 |
29 | 30 | {% endif %} 31 | 32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /patchwork/templates/patchwork/user-link.mail: -------------------------------------------------------------------------------- 1 | Hi, 2 | 3 | This email is to confirm that you own the email address: 4 | 5 | {{ confirmation.email }} 6 | 7 | So that you can add it to your Patchwork profile. You can confirm this 8 | email address by visiting the url: 9 | 10 | http://{{site.domain}}{% url 'confirm' key=confirmation.key %} 11 | 12 | Happy patchworking. 13 | -------------------------------------------------------------------------------- /patchwork/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/patchwork/templatetags/__init__.py -------------------------------------------------------------------------------- /patchwork/templatetags/listurl.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2008 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | from django.conf import settings 23 | from django.core.urlresolvers import reverse, NoReverseMatch 24 | from django import template 25 | from django.utils.encoding import smart_str 26 | from django.utils.html import escape 27 | 28 | from patchwork.filters import filterclasses 29 | 30 | 31 | register = template.Library() 32 | 33 | # params to preserve across views 34 | list_params = [c.param for c in filterclasses] + ['order', 'page'] 35 | 36 | 37 | class ListURLNode(template.defaulttags.URLNode): 38 | 39 | def __init__(self, kwargs): 40 | super(ListURLNode, self).__init__(None, [], {}, False) 41 | self.params = {} 42 | for (k, v) in kwargs.items(): 43 | if k in list_params: 44 | self.params[k] = v 45 | 46 | def render(self, context): 47 | view_name = template.Variable('list_view.view').resolve(context) 48 | kwargs = template.Variable('list_view.view_params').resolve(context) 49 | 50 | str = None 51 | try: 52 | str = reverse(view_name, args=[], kwargs=kwargs) 53 | except NoReverseMatch: 54 | try: 55 | project_name = settings.SETTINGS_MODULE.split('.')[0] 56 | str = reverse(project_name + '.' + view_name, 57 | args=[], kwargs=kwargs) 58 | except NoReverseMatch: 59 | raise 60 | 61 | if str is None: 62 | return '' 63 | 64 | params = [] 65 | try: 66 | qs_var = template.Variable('list_view.params') 67 | params = dict(qs_var.resolve(context)) 68 | except Exception: 69 | pass 70 | 71 | for (k, v) in self.params.items(): 72 | params[smart_str(k, 'ascii')] = v.resolve(context) 73 | 74 | if not params: 75 | return str 76 | 77 | return str + '?' + '&'.join( 78 | ['%s=%s' % (k, escape(v)) for (k, v) in list(params.items())]) 79 | 80 | 81 | @register.tag 82 | def listurl(parser, token): 83 | bits = token.contents.split(' ', 1) 84 | if len(bits) < 1: 85 | raise template.TemplateSyntaxError( 86 | "'%s' takes at least one argument (path to a view)" % bits[0]) 87 | kwargs = {} 88 | if len(bits) > 1: 89 | for arg in bits[1].split(','): 90 | if '=' in arg: 91 | k, v = arg.split('=', 1) 92 | k = k.strip() 93 | kwargs[k] = parser.compile_filter(v) 94 | else: 95 | raise template.TemplateSyntaxError( 96 | "'%s' requires name=value params" % bits[0]) 97 | return ListURLNode(kwargs) 98 | -------------------------------------------------------------------------------- /patchwork/templatetags/patch.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2008 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | from django import template 23 | from django.utils.safestring import mark_safe 24 | from django.template.defaultfilters import stringfilter 25 | 26 | 27 | register = template.Library() 28 | 29 | 30 | @register.filter(name='patch_tags') 31 | def patch_tags(patch, tag): 32 | count = getattr(patch, tag.attr_name) 33 | count_str = str(count) if count else '' 34 | class_str = "tag-%s" % tag.abbrev if count else '' 35 | title = '%d %s' % (count, tag.name) 36 | return mark_safe('%s' % 37 | (class_str, title, count_str)) 38 | 39 | 40 | @register.filter 41 | @stringfilter 42 | def msgid(patch): 43 | return patch.strip('<>') 44 | -------------------------------------------------------------------------------- /patchwork/templatetags/person.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2008 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | from django.core.urlresolvers import reverse 23 | from django import template 24 | from django.utils.html import escape 25 | from django.utils.safestring import mark_safe 26 | 27 | from patchwork.filters import SubmitterFilter 28 | 29 | 30 | register = template.Library() 31 | 32 | 33 | @register.filter 34 | def personify(person, project): 35 | 36 | if person.name: 37 | linktext = escape(person.name) 38 | else: 39 | linktext = escape(person.email) 40 | 41 | url = reverse('patch_list', kwargs={'project_id': project.linkname}) 42 | str = '%s' % \ 43 | (url, SubmitterFilter.param, escape(person.id), linktext) 44 | 45 | return mark_safe(str) 46 | -------------------------------------------------------------------------------- /patchwork/templatetags/syntax.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2008 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | import re 23 | 24 | from django import template 25 | from django.utils.html import escape 26 | from django.utils.safestring import mark_safe 27 | from django.utils.six.moves import map 28 | 29 | 30 | register = template.Library() 31 | 32 | 33 | def _compile(t): 34 | (r, str) = t 35 | return (re.compile(r, re.M | re.I), str) 36 | 37 | 38 | _patch_span_res = list(map(_compile, [ 39 | (r'^(Index:?|diff|\-\-\-|\+\+\+|\*\*\*) .*$', 'p_header'), 40 | (r'^\+.*$', 'p_add'), 41 | (r'^-.*$', 'p_del'), 42 | (r'^!.*$', 'p_mod'), 43 | ])) 44 | 45 | _patch_chunk_re = \ 46 | re.compile(r'^(@@ \-\d+(?:,\d+)? \+\d+(?:,\d+)? @@)(.*)$', re.M | re.I) 47 | 48 | _comment_span_res = list(map(_compile, [ 49 | (r'^\s*Signed-off-by: .*$', 'signed-off-by'), 50 | (r'^\s*Acked-by: .*$', 'acked-by'), 51 | (r'^\s*Nacked-by: .*$', 'nacked-by'), 52 | (r'^\s*Tested-by: .*$', 'tested-by'), 53 | (r'^\s*Reviewed-by: .*$', 'reviewed-by'), 54 | (r'^\s*From: .*$', 'from'), 55 | (r'^\s*>.*$', 'quote'), 56 | ])) 57 | 58 | _span = '%s' 59 | 60 | 61 | @register.filter 62 | def patchsyntax(patch): 63 | content = escape(patch.content).replace('\r\n', '\n') 64 | 65 | for (r, cls) in _patch_span_res: 66 | content = r.sub(lambda x: _span % (cls, x.group(0)), content) 67 | 68 | content = _patch_chunk_re.sub( 69 | lambda x: 70 | _span % ('p_chunk', x.group(1)) + ' ' + 71 | _span % ('p_context', x.group(2)), 72 | content) 73 | 74 | return mark_safe(content) 75 | 76 | 77 | @register.filter 78 | def commentsyntax(comment): 79 | content = escape(comment.content) 80 | 81 | for (r, cls) in _comment_span_res: 82 | content = r.sub(lambda x: _span % (cls, x.group(0)), content) 83 | 84 | return mark_safe(content) 85 | -------------------------------------------------------------------------------- /patchwork/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlespiau/patchwork/216bfcd41c57cc0119c8b1c917427d5a80c737eb/patchwork/tests/__init__.py -------------------------------------------------------------------------------- /patchwork/tests/mail/0008-git-rename.mbox: -------------------------------------------------------------------------------- 1 | From: "Yann E. MORIN" 2 | Subject: [Buildroot] [PATCH 01/11] package/rpi-userland: rename patches 3 | Date: Tue, 8 Oct 2013 22:09:47 +0000 4 | 5 | Rename patches to follow standard naming scheme. 6 | 7 | Signed-off-by: "Yann E. MORIN" 8 | --- 9 | ...d-pkgconfig-files.patch => rpi-userland-000-add-pkgconfig-files.patch} | 0 10 | ...erland-001-makefiles-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch} | 0 11 | 2 files changed, 0 insertions(+), 0 deletions(-) 12 | rename package/rpi-userland/{rpi-userland-add-pkgconfig-files.patch => rpi-userland-000-add-pkgconfig-files.patch} (100%) 13 | rename package/rpi-userland/{rpi-userland-makefiles-0001-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch => rpi-userland-001-makefiles-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch} (100%) 14 | 15 | diff --git a/package/rpi-userland/rpi-userland-add-pkgconfig-files.patch b/package/rpi-userland/rpi-userland-000-add-pkgconfig-files.patch 16 | similarity index 100% 17 | rename from package/rpi-userland/rpi-userland-add-pkgconfig-files.patch 18 | rename to package/rpi-userland/rpi-userland-000-add-pkgconfig-files.patch 19 | diff --git a/package/rpi-userland/rpi-userland-makefiles-0001-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch b/package/rpi-userland/rpi-userland-001-makefiles-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch 20 | similarity index 100% 21 | rename from package/rpi-userland/rpi-userland-makefiles-0001-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch 22 | rename to package/rpi-userland/rpi-userland-001-makefiles-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch 23 | -- 24 | 1.8.1.2 25 | -------------------------------------------------------------------------------- /patchwork/tests/mail/0009-git-rename-with-diff.mbox: -------------------------------------------------------------------------------- 1 | From: "Yann E. MORIN" 2 | Subject: [Buildroot] [PATCH 01/11] package/rpi-userland: rename patches 3 | Date: Tue, 8 Oct 2013 22:09:47 +0000 4 | 5 | Rename patches to follow standard naming scheme. 6 | 7 | Signed-off-by: "Yann E. MORIN" 8 | --- 9 | ...d-pkgconfig-files.patch => rpi-userland-000-add-pkgconfig-files.patch} | 0 10 | ...erland-001-makefiles-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch} | 0 11 | 2 files changed, 0 insertions(+), 0 deletions(-) 12 | rename package/rpi-userland/{rpi-userland-add-pkgconfig-files.patch => rpi-userland-000-add-pkgconfig-files.patch} (100%) 13 | rename package/rpi-userland/{rpi-userland-makefiles-0001-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch => rpi-userland-001-makefiles-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch} (100%) 14 | 15 | diff --git a/package/rpi-userland/rpi-userland-add-pkgconfig-files.patch b/package/rpi-userland/rpi-userland-000-add-pkgconfig-files.patch 16 | similarity index 100% 17 | rename from package/rpi-userland/rpi-userland-add-pkgconfig-files.patch 18 | rename to package/rpi-userland/rpi-userland-000-add-pkgconfig-files.patch 19 | @@ -100,7 +100,7 @@ 20 | a 21 | a 22 | -a 23 | +b 24 | c 25 | c 26 | c 27 | diff --git a/package/rpi-userland/rpi-userland-makefiles-0001-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch b/package/rpi-userland/rpi-userland-001-makefiles-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch 28 | similarity index 100% 29 | rename from package/rpi-userland/rpi-userland-makefiles-0001-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch 30 | rename to package/rpi-userland/rpi-userland-001-makefiles-cmake-vmcs.cmake-allow-to-override-VMCS_IN.patch 31 | -- 32 | 1.8.1.2 33 | -------------------------------------------------------------------------------- /patchwork/tests/mail/0011-no-newline-at-end-of-file.mbox: -------------------------------------------------------------------------------- 1 | Subject: [PATCH v3 5/5] selftests, powerpc: Add test for VPHN 2 | From: Greg Kurz 3 | To: Michael Ellerman 4 | Cc: Benjamin Herrenschmidt , 5 | linuxppc-dev@lists.ozlabs.org 6 | Date: Mon, 23 Feb 2015 16:14:44 +0100 7 | MIME-Version: 1.0 8 | Content-Type: text/plain; charset="utf-8" 9 | Content-Transfer-Encoding: 8bit 10 | 11 | The goal is to verify vphn_unpack_associativity() parses VPHN numbers 12 | correctly. We feed it with a variety of input values and compare with 13 | expected results. 14 | 15 | diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile 16 | index 1d5e7ad..476b8dd 100644 17 | --- a/tools/testing/selftests/powerpc/Makefile 18 | +++ b/tools/testing/selftests/powerpc/Makefile 19 | @@ -13,7 +13,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR 20 | 21 | export CC CFLAGS 22 | 23 | -TARGETS = pmu copyloops mm tm primitives stringloops 24 | +TARGETS = pmu copyloops mm tm primitives stringloops vphn 25 | 26 | endif 27 | 28 | diff --git a/tools/testing/selftests/powerpc/vphn/vphn.c b/tools/testing/selftests/powerpc/vphn/vphn.c 29 | new file mode 120000 30 | index 0000000..186b906 31 | --- /dev/null 32 | +++ b/tools/testing/selftests/powerpc/vphn/vphn.c 33 | @@ -0,0 +1 @@ 34 | +../../../../../arch/powerpc/mm/vphn.c 35 | \ No newline at end of file 36 | diff --git a/tools/testing/selftests/powerpc/vphn/vphn.h b/tools/testing/selftests/powerpc/vphn/vphn.h 37 | new file mode 120000 38 | index 0000000..7131efe 39 | --- /dev/null 40 | +++ b/tools/testing/selftests/powerpc/vphn/vphn.h 41 | @@ -0,0 +1 @@ 42 | +../../../../../arch/powerpc/mm/vphn.h 43 | \ No newline at end of file 44 | 45 | 46 | -------------------------------------------------------------------------------- /patchwork/tests/mail/series/0001-single-mail.mbox: -------------------------------------------------------------------------------- 1 | From: Chris Wilson 2 | To: 3 | Date: Thu, 22 May 2014 09:44:40 +0100 4 | Message-ID: <1400748280-26449-1-git-send-email-chris@chris-wilson.co.uk> 5 | X-Mailer: git-send-email 2.0.0.rc2 6 | X-Authenticated-User: chris.alporthouse@surfanytime.net 7 | Subject: [Intel-gfx] [PATCH] drm/i915: Hold CRTC lock whilst freezing the 8 | planes 9 | X-BeenThere: intel-gfx@lists.freedesktop.org 10 | X-Mailman-Version: 2.1.15 11 | Precedence: list 12 | List-Id: Intel graphics driver community testing & development 13 | 14 | List-Unsubscribe: , 15 | 16 | List-Archive: 17 | List-Post: 18 | List-Help: 19 | List-Subscribe: , 20 | 21 | Content-Type: text/plain; charset="us-ascii" 22 | Content-Transfer-Encoding: 7bit 23 | Errors-To: intel-gfx-bounces@lists.freedesktop.org 24 | Sender: Intel-gfx 25 | Return-Path: intel-gfx-bounces@lists.freedesktop.org 26 | X-MS-Exchange-Organization-AVStamp-Mailbox: NAI;56075518;0;novirus 27 | X-MS-Exchange-Organization-AuthSource: FMSMSX107.amr.corp.intel.com 28 | X-MS-Exchange-Organization-AuthAs: Anonymous 29 | Content-Length: 1472 30 | 31 | Daniel keeps on ramping up the warning level of the DRM and our display 32 | core to make it complain whenever the locking rules are not followed. 33 | This caught 34 | 35 | commit 24576d23976746cb52e7700c4cadbf4bc1bc3472 36 | Author: Jesse Barnes 37 | Date: Tue Mar 26 09:25:45 2013 -0700 38 | 39 | drm/i915: enable VT switchless resume v3 40 | 41 | introducing an unlocked access to the CRTC whilst disabling it for 42 | suspend. 43 | 44 | Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=78114 45 | Signed-off-by: Chris Wilson 46 | Cc: Jesse Barnes 47 | Reviewed-by: Daniel Vetter 48 | --- 49 | drivers/gpu/drm/i915/i915_drv.c | 5 ++++- 50 | 1 file changed, 4 insertions(+), 1 deletion(-) 51 | 52 | diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c 53 | index 591762c26af4..8450569ff42c 100644 54 | --- a/drivers/gpu/drm/i915/i915_drv.c 55 | +++ b/drivers/gpu/drm/i915/i915_drv.c 56 | @@ -532,8 +532,11 @@ static int i915_drm_freeze(struct drm_device *dev) 57 | * for _thaw. 58 | */ 59 | mutex_lock(&dev->mode_config.mutex); 60 | - for_each_crtc(dev, crtc) 61 | + for_each_crtc(dev, crtc) { 62 | + mutex_lock(&crtc->mutex); 63 | dev_priv->display.crtc_disable(crtc); 64 | + mutex_unlock(&crtc->mutex); 65 | + } 66 | mutex_unlock(&dev->mode_config.mutex); 67 | 68 | intel_modeset_suspend_hw(dev); 69 | -- 70 | 2.0.0.rc2 71 | 72 | _______________________________________________ 73 | Intel-gfx mailing list 74 | Intel-gfx@lists.freedesktop.org 75 | http://lists.freedesktop.org/mailman/listinfo/intel-gfx 76 | -------------------------------------------------------------------------------- /patchwork/tests/mail/series/0010-multiple-mails-cover-letter.mbox: -------------------------------------------------------------------------------- 1 | MIME-Version: 1.0 2 | X-Original-To: intel-gfx@lists.freedesktop.org 3 | Delivered-To: intel-gfx@lists.freedesktop.org 4 | From: Damien Lespiau 5 | To: 6 | Date: Tue, 13 May 2014 23:32:20 +0100 7 | Message-ID: <1400020344-17248-1-git-send-email-damien.lespiau@intel.com> 8 | X-Mailer: git-send-email 1.8.3.1 9 | Subject: [Intel-gfx] [PATCH 0/4] for_each_{intel_,}crtc v2 10 | X-BeenThere: intel-gfx@lists.freedesktop.org 11 | X-Mailman-Version: 2.1.15 12 | Precedence: list 13 | List-Id: Intel graphics driver community testing & development 14 | 15 | List-Unsubscribe: , 16 | 17 | List-Archive: 18 | List-Post: 19 | List-Help: 20 | List-Subscribe: , 21 | 22 | Content-Type: text/plain; charset="us-ascii" 23 | Content-Transfer-Encoding: 7bit 24 | Errors-To: intel-gfx-bounces@lists.freedesktop.org 25 | Sender: Intel-gfx 26 | Return-Path: intel-gfx-bounces@lists.freedesktop.org 27 | X-MS-Exchange-Organization-AVStamp-Mailbox: NAI;56075428;0;novirus 28 | X-MS-Exchange-Organization-AuthSource: FMSMSX106.amr.corp.intel.com 29 | X-MS-Exchange-Organization-AuthAs: Anonymous 30 | Content-Length: 1048 31 | 32 | With Daniel's help to figure out an arcane corner of coccinelle, here is v2 of 33 | a series introducing macros to iterate through the CRTCs instead of using 34 | list_for_each_entry() and mode_config.crtc_list, a tiny bit more readable and 35 | easier to recall. 36 | 37 | Damien Lespiau (4): 38 | drm/i915: Introduce a for_each_intel_crtc() macro 39 | drm/i915: Use for_each_intel_crtc() when iterating through intel_crtcs 40 | drm/i915: Introduce a for_each_crtc() macro 41 | drm/i915: Use for_each_crtc() when iterating through the CRTCs 42 | 43 | drivers/gpu/drm/i915/i915_debugfs.c | 4 +- 44 | drivers/gpu/drm/i915/i915_drv.c | 2 +- 45 | drivers/gpu/drm/i915/i915_drv.h | 6 +++ 46 | drivers/gpu/drm/i915/intel_display.c | 71 +++++++++++++++--------------------- 47 | drivers/gpu/drm/i915/intel_fbdev.c | 6 +-- 48 | drivers/gpu/drm/i915/intel_pm.c | 12 +++--- 49 | 6 files changed, 47 insertions(+), 54 deletions(-) 50 | 51 | -- 52 | 1.8.3.1 53 | 54 | _______________________________________________ 55 | Intel-gfx mailing list 56 | Intel-gfx@lists.freedesktop.org 57 | http://lists.freedesktop.org/mailman/listinfo/intel-gfx 58 | -------------------------------------------------------------------------------- /patchwork/tests/mail/series/0011-multiple-mails-cover-letter.mbox: -------------------------------------------------------------------------------- 1 | MIME-Version: 1.0 2 | X-Original-To: intel-gfx@lists.freedesktop.org 3 | Delivered-To: intel-gfx@lists.freedesktop.org 4 | From: Damien Lespiau 5 | To: 6 | Date: Tue, 13 May 2014 23:32:21 +0100 7 | Message-ID: <1400020344-17248-2-git-send-email-damien.lespiau@intel.com> 8 | X-Mailer: git-send-email 1.8.3.1 9 | In-Reply-To: <1400020344-17248-1-git-send-email-damien.lespiau@intel.com> 10 | References: <1400020344-17248-1-git-send-email-damien.lespiau@intel.com> 11 | Subject: [Intel-gfx] [PATCH 1/4] drm/i915: Introduce a for_each_intel_crtc() 12 | macro 13 | X-BeenThere: intel-gfx@lists.freedesktop.org 14 | X-Mailman-Version: 2.1.15 15 | Precedence: list 16 | List-Id: Intel graphics driver community testing & development 17 | 18 | List-Unsubscribe: , 19 | 20 | List-Archive: 21 | List-Post: 22 | List-Help: 23 | List-Subscribe: , 24 | 25 | Content-Type: text/plain; charset="us-ascii" 26 | Content-Transfer-Encoding: 7bit 27 | Errors-To: intel-gfx-bounces@lists.freedesktop.org 28 | Sender: Intel-gfx 29 | Return-Path: intel-gfx-bounces@lists.freedesktop.org 30 | X-MS-Exchange-Organization-AVStamp-Mailbox: NAI;56075407;0;novirus 31 | X-MS-Exchange-Organization-AuthSource: FMSMSX103.amr.corp.intel.com 32 | X-MS-Exchange-Organization-AuthAs: Anonymous 33 | Content-Length: 1117 34 | 35 | Fed up with having that long list_for_each_entry() invocation? 36 | 37 | Use for_each_intel_crtc()! 38 | 39 | Signed-off-by: Damien Lespiau 40 | --- 41 | drivers/gpu/drm/i915/i915_drv.h | 3 +++ 42 | 1 file changed, 3 insertions(+) 43 | 44 | diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h 45 | index 8885455..edf7299 100644 46 | --- a/drivers/gpu/drm/i915/i915_drv.h 47 | +++ b/drivers/gpu/drm/i915/i915_drv.h 48 | @@ -163,6 +163,9 @@ enum hpd_pin { 49 | #define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++) 50 | #define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++) 51 | 52 | +#define for_each_intel_crtc(dev, intel_crtc) \ 53 | + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) 54 | + 55 | #define for_each_encoder_on_crtc(dev, __crtc, intel_encoder) \ 56 | list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \ 57 | if ((intel_encoder)->base.crtc == (__crtc)) 58 | -- 59 | 1.8.3.1 60 | 61 | _______________________________________________ 62 | Intel-gfx mailing list 63 | Intel-gfx@lists.freedesktop.org 64 | http://lists.freedesktop.org/mailman/listinfo/intel-gfx 65 | -------------------------------------------------------------------------------- /patchwork/tests/mail/series/0013-multiple-mails-cover-letter.mbox: -------------------------------------------------------------------------------- 1 | MIME-Version: 1.0 2 | X-IronPort-Anti-Spam-Filtered: true 3 | X-IronPort-Anti-Spam-Result: AoQBAD+cclOD/NKxnGdsb2JhbABZg1W/AxmIYhYOAQEBAQEICwkJFCiCJgEBAQMBAQEkEwwKHg4DAQIGAQFABAQIAwEjAS8ZBYg8BQjGcxeOa4QqBJZygl6BPYVUg3qLPA 4 | X-IronPort-AV: E=Sophos;i="4.97,1047,1389772800"; 5 | d="scan'208";a="174440116" 6 | X-Original-To: intel-gfx@lists.freedesktop.org 7 | Delivered-To: intel-gfx@lists.freedesktop.org 8 | From: Damien Lespiau 9 | To: 10 | Date: Tue, 13 May 2014 23:32:23 +0100 11 | Message-ID: <1400020344-17248-4-git-send-email-damien.lespiau@intel.com> 12 | X-Mailer: git-send-email 1.8.3.1 13 | In-Reply-To: <1400020344-17248-1-git-send-email-damien.lespiau@intel.com> 14 | References: <1400020344-17248-1-git-send-email-damien.lespiau@intel.com> 15 | Subject: [Intel-gfx] [PATCH 3/4] drm/i915: Introduce a for_each_crtc() macro 16 | X-BeenThere: intel-gfx@lists.freedesktop.org 17 | X-Mailman-Version: 2.1.15 18 | Precedence: list 19 | List-Id: Intel graphics driver community testing & development 20 | 21 | List-Unsubscribe: , 22 | 23 | List-Archive: 24 | List-Post: 25 | List-Help: 26 | List-Subscribe: , 27 | 28 | Content-Type: text/plain; charset="us-ascii" 29 | Content-Transfer-Encoding: 7bit 30 | Errors-To: intel-gfx-bounces@lists.freedesktop.org 31 | Sender: Intel-gfx 32 | Return-Path: intel-gfx-bounces@lists.freedesktop.org 33 | X-MS-Exchange-Organization-AVStamp-Mailbox: NAI;56075428;0;novirus 34 | X-MS-Exchange-Organization-AuthSource: ORSMSX101.amr.corp.intel.com 35 | X-MS-Exchange-Organization-AuthAs: Anonymous 36 | Content-Length: 929 37 | 38 | Signed-off-by: Damien Lespiau 39 | --- 40 | drivers/gpu/drm/i915/i915_drv.h | 3 +++ 41 | 1 file changed, 3 insertions(+) 42 | 43 | diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h 44 | index edf7299..4006dfe 100644 45 | --- a/drivers/gpu/drm/i915/i915_drv.h 46 | +++ b/drivers/gpu/drm/i915/i915_drv.h 47 | @@ -163,6 +163,9 @@ enum hpd_pin { 48 | #define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++) 49 | #define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++) 50 | 51 | +#define for_each_crtc(dev, crtc) \ 52 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 53 | + 54 | #define for_each_intel_crtc(dev, intel_crtc) \ 55 | list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) 56 | 57 | -- 58 | 1.8.3.1 59 | 60 | _______________________________________________ 61 | Intel-gfx mailing list 62 | Intel-gfx@lists.freedesktop.org 63 | http://lists.freedesktop.org/mailman/listinfo/intel-gfx 64 | -------------------------------------------------------------------------------- /patchwork/tests/patches/0001-add-line.patch: -------------------------------------------------------------------------------- 1 | diff --git a/meep.text b/meep.text 2 | index 3d75d48..a57f4dd 100644 3 | --- a/meep.text 4 | +++ b/meep.text 5 | @@ -1,1 +1,2 @@ 6 | meep 7 | +meep 8 | -------------------------------------------------------------------------------- /patchwork/tests/patches/0002-utf-8.patch: -------------------------------------------------------------------------------- 1 | diff --git a/meep.text b/meep.text 2 | index 3d75d48..a57f4dd 100644 3 | --- a/meep.text 4 | +++ b/meep.text 5 | @@ -1,1 +1,2 @@ 6 | meep 7 | +meëp 8 | -------------------------------------------------------------------------------- /patchwork/tests/test_comment.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2017 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from django.test import TestCase 21 | 22 | from patchwork.models import Comment 23 | 24 | NOT_TAGS = \ 25 | """ 26 | Hey, hi, hello 27 | 28 | message 29 | 30 | Nonsense-by: zzz 31 | """ 32 | 33 | TAGS = \ 34 | """ 35 | Reviewed-by: aaa 36 | Fixes: foo 37 | Signed-off-by: bbb 38 | Acked-by: bar 39 | Tested-by: ccc 40 | Nacked-by: baz 41 | Reported-by: ddd 42 | """ 43 | 44 | 45 | class CommentTest(TestCase): 46 | def testPatchResponse(self): 47 | comment = Comment() 48 | comment.content = NOT_TAGS + TAGS 49 | 50 | reference_tags = TAGS.split() 51 | actual_tags = comment.patch_responses().split() 52 | 53 | self.assertListEqual(sorted(reference_tags), sorted(actual_tags)) 54 | -------------------------------------------------------------------------------- /patchwork/tests/test_confirm.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2011 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from django.contrib.auth.models import User 21 | from django.core.urlresolvers import reverse 22 | from django.test import TestCase 23 | 24 | from patchwork.models import EmailConfirmation, Person 25 | 26 | 27 | def _confirmation_url(conf): 28 | return reverse('confirm', kwargs={'key': conf.key}) 29 | 30 | 31 | class TestUser(object): 32 | username = 'testuser' 33 | email = 'test@example.com' 34 | secondary_email = 'test2@example.com' 35 | password = None 36 | 37 | def __init__(self): 38 | self.password = User.objects.make_random_password() 39 | self.user = User.objects.create_user(self.username, 40 | self.email, self.password) 41 | 42 | 43 | class InvalidConfirmationTest(TestCase): 44 | 45 | def setUp(self): 46 | EmailConfirmation.objects.all().delete() 47 | Person.objects.all().delete() 48 | self.user = TestUser() 49 | self.conf = EmailConfirmation(type='userperson', 50 | email=self.user.secondary_email, 51 | user=self.user.user) 52 | self.conf.save() 53 | 54 | def testInactiveConfirmation(self): 55 | self.conf.active = False 56 | self.conf.save() 57 | response = self.client.get(_confirmation_url(self.conf)) 58 | self.assertEqual(response.status_code, 200) 59 | self.assertTemplateUsed(response, 'patchwork/confirm-error.html') 60 | self.assertEqual(response.context['error'], 'inactive') 61 | self.assertEqual(response.context['conf'], self.conf) 62 | 63 | def testExpiredConfirmation(self): 64 | self.conf.date -= self.conf.validity 65 | self.conf.save() 66 | response = self.client.get(_confirmation_url(self.conf)) 67 | self.assertEqual(response.status_code, 200) 68 | self.assertTemplateUsed(response, 'patchwork/confirm-error.html') 69 | self.assertEqual(response.context['error'], 'expired') 70 | self.assertEqual(response.context['conf'], self.conf) 71 | -------------------------------------------------------------------------------- /patchwork/tests/test_db.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2016 Damien Lespiau 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import sys 24 | from django.utils.six import StringIO 25 | from contextlib import contextmanager 26 | 27 | from django.core import management 28 | from django.test import TestCase 29 | 30 | 31 | @contextmanager 32 | def capture(command, *args, **kwargs): 33 | out, sys.stdout = sys.stdout, StringIO() 34 | command(*args, **kwargs) 35 | sys.stdout.seek(0) 36 | yield sys.stdout.read() 37 | sys.stdout = out 38 | 39 | 40 | def makemigrations(): 41 | management.call_command('makemigrations') 42 | 43 | 44 | class MigrationTest(TestCase): 45 | 46 | def testPendingMigration(self): 47 | """Make sure there's no pending migration.""" 48 | 49 | with capture(makemigrations) as output: 50 | self.assertEqual(output, "No changes detected\n") 51 | -------------------------------------------------------------------------------- /patchwork/tests/test_fields.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2016 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from django.test import SimpleTestCase 21 | 22 | from patchwork import fields 23 | 24 | 25 | class TestHashField(SimpleTestCase): 26 | 27 | def test_n_bytes(self): 28 | """Sanity check the hashing algorithm. 29 | 30 | Changing this can change our database schema. 31 | """ 32 | field = fields.HashField() 33 | self.assertEqual(field.n_bytes, 40) 34 | -------------------------------------------------------------------------------- /patchwork/tests/test_filters.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2011 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from django.test import TestCase 21 | 22 | from patchwork.tests.utils import defaults 23 | 24 | 25 | class FilterQueryStringTest(TestCase): 26 | 27 | def testFilterQSEscaping(self): 28 | """test that filter fragments in a query string are properly escaped, 29 | and stray ampersands don't get reflected back in the filter 30 | links""" 31 | project = defaults.project 32 | defaults.project.save() 33 | url = '/project/%s/list/?submitter=a%%26b=c' % project.linkname 34 | response = self.client.get(url) 35 | self.assertEqual(response.status_code, 200) 36 | self.assertNotContains(response, 'submitter=a&b=c') 37 | self.assertNotContains(response, 'submitter=a&b=c') 38 | 39 | def testUTF8QSHandling(self): 40 | """test that non-ascii characters can be handled by the filter 41 | code""" 42 | project = defaults.project 43 | defaults.project.save() 44 | url = '/project/%s/list/?submitter=%%E2%%98%%83' % project.linkname 45 | response = self.client.get(url) 46 | self.assertEqual(response.status_code, 200) 47 | -------------------------------------------------------------------------------- /patchwork/tests/test_person.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2013 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | import json 23 | from django.test import TestCase 24 | from django.utils.six.moves import map, range 25 | 26 | from patchwork.models import Person 27 | 28 | 29 | class SubmitterCompletionTest(TestCase): 30 | 31 | def setUp(self): 32 | self.people = [ 33 | Person(name="Test Name", email="test1@example.com"), 34 | Person(email="test2@example.com"), 35 | ] 36 | list(map(lambda p: p.save(), self.people)) 37 | 38 | def testNameComplete(self): 39 | response = self.client.get('/submitter/', {'q': 'name'}) 40 | self.assertEqual(response.status_code, 200) 41 | data = json.loads(response.content.decode()) 42 | self.assertEqual(len(data), 1) 43 | self.assertEqual(data[0]['name'], 'Test Name') 44 | 45 | def testEmailComplete(self): 46 | response = self.client.get('/submitter/', {'q': 'test2'}) 47 | self.assertEqual(response.status_code, 200) 48 | data = json.loads(response.content.decode()) 49 | self.assertEqual(len(data), 1) 50 | self.assertEqual(data[0]['email'], 'test2@example.com') 51 | 52 | def testCompleteLimit(self): 53 | for i in range(3, 10): 54 | person = Person(email='test%d@example.com' % i) 55 | person.save() 56 | response = self.client.get('/submitter/', {'q': 'test', 'l': 5}) 57 | self.assertEqual(response.status_code, 200) 58 | data = json.loads(response.content.decode()) 59 | self.assertEqual(len(data), 5) 60 | 61 | 62 | class PersonModelTest(TestCase): 63 | 64 | def testEmailNameQuoted(self): 65 | p = Person(name="Name, Test", email="test@example.com") 66 | self.assertEqual(p.email_name(), '"Name, Test" ') 67 | -------------------------------------------------------------------------------- /patchwork/tests/test_user_browser.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2015 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from patchwork.tests.browser import SeleniumTestCase 21 | from patchwork.tests.test_user import TestUser 22 | 23 | 24 | class LoginTestCase(SeleniumTestCase): 25 | 26 | def setUp(self): 27 | super(LoginTestCase, self).setUp() 28 | self.user = TestUser() 29 | 30 | def test_default_focus(self): 31 | self.get('/user/login/') 32 | self.wait_until_focused('#id_username') 33 | 34 | def test_login(self): 35 | self.get('/user/login/') 36 | self.enter_text('username', self.user.username) 37 | self.enter_text('password', self.user.password) 38 | self.click('input[value="Login"]') 39 | header = self.wait_until_visible('h1') 40 | self.assertEqual(header.text, 'Your Profile') 41 | self.assertTrue(self.user.username in self.selenium.title) 42 | -------------------------------------------------------------------------------- /patchwork/threadlocalrequest.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | threadlocals middleware 5 | ~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | make the request object everywhere available (e.g. in model instance). 8 | 9 | based on: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser 10 | 11 | Put this into your settings: 12 | -------------------------------------------------------------------------- 13 | MIDDLEWARE_CLASSES = ( 14 | ... 15 | 'django_tools.middlewares.ThreadLocal.ThreadLocalMiddleware', 16 | ... 17 | ) 18 | -------------------------------------------------------------------------- 19 | 20 | 21 | Usage: 22 | -------------------------------------------------------------------------- 23 | from django_tools.middlewares import ThreadLocal 24 | 25 | # Get the current request object: 26 | request = ThreadLocal.get_current_request() 27 | 28 | # You can get the current user directly with: 29 | user = ThreadLocal.get_current_user() 30 | -------------------------------------------------------------------------- 31 | 32 | :copyleft: 2009-2011 by the django-tools team, see AUTHORS for more 33 | details. 34 | :license: GNU GPL v3 or above, see LICENSE for more details. 35 | """ 36 | 37 | from __future__ import absolute_import, division, print_function 38 | 39 | 40 | try: 41 | from threading import local 42 | except ImportError: 43 | from django.utils._threading_local import local 44 | 45 | 46 | _thread_locals = local() 47 | 48 | 49 | def get_current_request(): 50 | """ returns the request object for this thread """ 51 | return getattr(_thread_locals, "request", None) 52 | 53 | 54 | def get_current_user(): 55 | """ returns the current user, if exist, otherwise returns None """ 56 | request = get_current_request() 57 | if request: 58 | return getattr(request, "user", None) 59 | 60 | 61 | class ThreadLocalRequestMiddleware(object): 62 | """ Simple middleware that adds the request object in thread local 63 | storage.""" 64 | def process_request(self, request): 65 | _thread_locals.request = request 66 | 67 | def process_response(self, request, response): 68 | if hasattr(_thread_locals, 'request'): 69 | del _thread_locals.request 70 | return response 71 | -------------------------------------------------------------------------------- /patchwork/views/project.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2009 Jeremy Kerr 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from __future__ import absolute_import 21 | 22 | from django.contrib.auth.models import User 23 | from django.shortcuts import render_to_response, get_object_or_404 24 | 25 | from patchwork.models import Patch, Project, Series 26 | 27 | 28 | def project(request, project_id): 29 | project = get_object_or_404(Project, linkname=project_id) 30 | context = {'project': project} 31 | 32 | context['maintainers'] = User.objects.filter( 33 | profile__maintainer_projects=project) 34 | context['n_patches'] = Patch.objects.filter(project=project, 35 | archived=False).count() 36 | context['n_archived_patches'] = Patch.objects.filter(project=project, 37 | archived=True).count() 38 | context['n_series'] = Series.objects.filter(project=project).count() 39 | 40 | return render_to_response('patchwork/project.html', context) 41 | -------------------------------------------------------------------------------- /patchwork/views/series.py: -------------------------------------------------------------------------------- 1 | # Patchwork - automated patch tracking system 2 | # Copyright (C) 2014 Intel Corporation 3 | # 4 | # This file is part of the Patchwork package. 5 | # 6 | # Patchwork is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Patchwork is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Patchwork; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | from django.conf import settings 21 | from django.shortcuts import render, get_object_or_404, get_list_or_404 22 | from django.views.generic import View 23 | from patchwork.models import Project, Series, SeriesRevision, TestResult 24 | from patchwork.permissions import Can 25 | 26 | 27 | class SeriesListView(View): 28 | 29 | def get(self, request, *args, **kwargs): 30 | project = get_object_or_404(Project, linkname=kwargs['project']) 31 | 32 | return render(request, 'patchwork/series-list.html', { 33 | 'project': project, 34 | 'is_editable': Can(request.user).edit(project), 35 | 'default_patches_per_page': settings.DEFAULT_PATCHES_PER_PAGE, 36 | }) 37 | 38 | 39 | class SeriesView(View): 40 | 41 | def get(self, request, *args, **kwargs): 42 | series = get_object_or_404(Series, pk=kwargs['series']) 43 | revisions = get_list_or_404(SeriesRevision, series=series) 44 | project = series.project 45 | for revision in revisions: 46 | revision.patch_list = revision.ordered_patches().\ 47 | select_related('state', 'submitter') 48 | revision.test_results = TestResult.objects \ 49 | .filter(revision=revision, patch=None) \ 50 | .order_by('test__name').select_related('test') 51 | 52 | return render(request, 'patchwork/series.html', { 53 | 'series': series, 54 | 'project': project, 55 | 'is_editable': Can(request.user).edit(project), 56 | 'is_retestable': Can(request.user).retest(series), 57 | 'cover_letter': revision.cover_letter, 58 | 'revisions': revisions, 59 | }) 60 | -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}404: File not found{% endblock %} 4 | {% block heading %}404: File not found{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

The page URL requested ({{ request_path }}) does not exist.

9 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /templates/registration/password_change_done.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Password Change Complete{% endblock %} 4 | {% block heading %}Password Change Complete{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

Your password has been changed successfully.

9 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /templates/registration/password_change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Password Change{% endblock %} 4 | {% block heading %}Password Change{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

Please enter your old password, for security's sake, and then enter your new 9 | password twice so we can verify you typed it in correctly.

10 | 11 |
12 | {% csrf_token %} 13 | 14 | 15 | 16 | {% if form.errors %} 17 | 18 | 20 | {% endif %} 21 | 22 | 23 | {% for field in form %} 24 | 25 | 26 | 33 | 34 | {% endfor %} 35 | 36 | 37 | 40 | 41 | 42 |
Please correct the errors below.

19 |
{{ field.label_tag }} 27 | {{ field.errors }} 28 | {{ field }} 29 | {% if field.help_text %} 30 |
{{ field.help_text }}
31 | {% endif %} 32 |
38 | 39 |
43 | 44 |
45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /templates/registration/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Password reset completed{% endblock %} 4 | {% block heading %}Password reset completed{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

Your password has been set. You may go ahead and log in now.

9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/registration/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Password reset confirmation{% endblock %} 4 | {% block heading %}Password reset confirmation{% endblock %} 5 | 6 | {% block body %} 7 | 8 | {% if validlink %} 9 |

Your username, in case you've forgotten: {{ form.user.get_username }}

10 |

Please enter your new password twice so we can verify you typed it in 11 | correctly

12 | 13 |
14 | {% csrf_token %} 15 | 16 | 17 | 18 | {% if form.errors %} 19 | 20 | 22 | {% endif %} 23 | 24 | 25 | {% for field in form %} 26 | 27 | 28 | 35 | 36 | {% endfor %} 37 | 38 | 39 | 42 | 43 | 44 |
Please correct the errors below.

21 |
{{ field.label_tag }} 29 | {{ field.errors }} 30 | {{ field }} 31 | {% if field.help_text %} 32 |
{{ field.help_text }}
33 | {% endif %} 34 |
40 | 41 |
45 | 46 |
47 | {% else %} 48 |

The password reset link was invalid, possibly because it has already 49 | been used. Please request a new password reset.

50 | {% endif %} 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /templates/registration/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Password reset{% endblock %} 4 | {% block heading %}Password reset{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

We have emailed you instructions for setting your password. 9 | You should be receiving them shortly.

10 |

If you don't receive an email, please make sure you've entered the 11 | address you registered with, and check your spam folder

12 | 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /templates/registration/password_reset_email.html: -------------------------------------------------------------------------------- 1 | {% autoescape off %}Hi, 2 | 3 | You are receiving this email because you requested a password reset for 4 | your user account on the Patchwork patch-tracking system. 5 | Please visit the following url and choose a new password: 6 | 7 | {% block reset_link %} 8 | {{ protocol }}://{{domain}}{% url 'password_reset_confirm' uidb64=uid token=token %} 9 | {% endblock %} 10 | 11 | Happy patchworking. 12 | {% endautoescape %} 13 | -------------------------------------------------------------------------------- /templates/registration/password_reset_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Password reset{% endblock %} 4 | {% block heading %}Password reset{% endblock %} 5 | 6 | {% block body %} 7 | 8 |

Forgotten your password? Enter your email address below, and we will 9 | email instructions for setting a new one.

10 | 11 |
12 | {% csrf_token %} 13 | 14 | 15 | 16 | {% if form.errors %} 17 | 18 | 20 | {% endif %} 21 | 22 | 23 | {% for field in form %} 24 | 25 | 26 | 33 | 34 | {% endfor %} 35 | 36 | 37 | 40 | 41 | 42 |
Please correct the errors below.

19 |
{{ field.label_tag }} 27 | {{ field.errors }} 28 | {{ field }} 29 | {% if field.help_text %} 30 |
{{ field.help_text }}
31 | {% endif %} 32 |
38 | 39 |
43 | 44 |
45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /tests/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Wed Nov 04 2015 14:49:54 GMT+0000 (GMT) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | '../htdocs/js/jquery-1.10.1.min.js', 19 | '../htdocs/js/selectize.min.js', 20 | '../htdocs/js/patchwork.js', 21 | 'test_patchwork.js' 22 | ], 23 | 24 | 25 | // list of files to exclude 26 | exclude: [ 27 | ], 28 | 29 | 30 | // preprocess matching files before serving them to the browser 31 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 32 | preprocessors: { 33 | }, 34 | 35 | 36 | // test results reporter to use 37 | // possible values: 'dots', 'progress' 38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 39 | reporters: ['progress'], 40 | 41 | 42 | // web server port 43 | port: 9876, 44 | 45 | 46 | // enable / disable colors in the output (reporters and logs) 47 | colors: true, 48 | 49 | 50 | // level of logging 51 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 52 | logLevel: config.LOG_INFO, 53 | 54 | 55 | // enable / disable watching file and executing tests whenever any file changes 56 | autoWatch: true, 57 | 58 | 59 | // start these browsers 60 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 61 | browsers: ['ChromiumHeadlessNoSandbox'], 62 | 63 | customLaunchers: { 64 | ChromiumHeadlessNoSandbox: { 65 | base: 'ChromiumHeadless', 66 | flags: ['--no-sandbox'] 67 | } 68 | }, 69 | 70 | 71 | // Continuous Integration mode 72 | // if true, Karma captures browsers, runs the tests and exits 73 | singleRun: false, 74 | 75 | // Concurrency level 76 | // how many browser should be started simultanous 77 | concurrency: Infinity 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | tox 2 | -------------------------------------------------------------------------------- /tests/test_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | script_dir=$(cd `dirname $0`; pwd) 4 | root_dir=`dirname $script_dir` 5 | tests_dir=$root_dir/tests 6 | 7 | update_virtualenv() 8 | { 9 | directory=$1 10 | requirements=$2 11 | 12 | [ -d "$directory" ] || virtualenv "$directory" 13 | source $directory/bin/activate 14 | pip install -r $requirements 15 | } 16 | 17 | update_virtualenv venv $tests_dir/requirements.txt 18 | 19 | tox --recreate 20 | $tests_dir/test_js.sh 21 | 22 | deactivate 23 | -------------------------------------------------------------------------------- /tests/test_js.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | script_dir=$(cd `dirname $0`; pwd) 4 | root_dir=`dirname $script_dir` 5 | tests=$root_dir/tests 6 | 7 | $tests/test_jshint.sh 8 | $tests/test_karma.sh 9 | -------------------------------------------------------------------------------- /tests/test_jshint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | script_dir=$(cd `dirname $0`; pwd) 4 | root_dir=`dirname $script_dir` 5 | js=$root_dir/htdocs/js 6 | tests=$root_dir/tests 7 | files="$js/patchwork.js $js/common.js $js/bundle.js $tests/test_*.js" 8 | files="$files $js/series-list.js" 9 | 10 | jshint $files 11 | -------------------------------------------------------------------------------- /tests/test_karma.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | script_dir=$(cd `dirname $0`; pwd) 4 | root_dir=`dirname $script_dir` 5 | tests=$root_dir/tests 6 | 7 | karma start $tests/karma.conf.js --single-run 8 | -------------------------------------------------------------------------------- /tests/test_patchwork.js: -------------------------------------------------------------------------------- 1 | describe("patch_strip_series_marker()", function() { 2 | var fn = pw.patch_strip_series_marker; 3 | 4 | it("should strip series markers", function() { 5 | res = fn("[1/2] foo"); 6 | expect(res.order).toBe("1"); 7 | expect(res.name).toBe("foo"); 8 | }); 9 | 10 | it("infer order when there's no series markers", function() { 11 | res = fn("foo"); 12 | expect(res.order).toBe("1"); 13 | expect(res.name).toBe("foo"); 14 | 15 | res = fn("[i-g-t] foo"); 16 | expect(res.order).toBe("1"); 17 | expect(res.name).toBe("[i-g-t] foo"); 18 | }); 19 | 20 | it("strip only series markers", function() { 21 | res = fn("[v2,2/3] foo"); 22 | expect(res.order).toBe("2"); 23 | expect(res.name).toBe("[v2] foo"); 24 | }); 25 | 26 | it("don't fail if the series name contains '[' or ']'", function() { 27 | res = fn("[2/3] f[o]o"); 28 | expect(res.order).toBe("2"); 29 | expect(res.name).toBe("f[o]o"); 30 | 31 | res = fn("f[o]o"); 32 | expect(res.order).toBe("1"); 33 | expect(res.name).toBe("f[o]o"); 34 | 35 | /* That's really pushing it, but we never know */ 36 | res = fn("f[2/3]o"); 37 | expect(res.order).toBe("1"); 38 | expect(res.name).toBe("f[2/3]o"); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /tools/patchwork-update-commits: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | toolsdir="$(dirname "$0")" 4 | pwpath="${toolsdir}"/../patchwork 5 | 6 | if [ "$#" -lt 1 ] 7 | then 8 | echo "usage: $0 " >&2 9 | exit 1 10 | fi 11 | 12 | git rev-list --reverse "$@" | 13 | while read commit 14 | do 15 | hash=$(git show "$commit" | python $pwpath/parser.py -#) 16 | $pwpath/bin/pwclient update -s Accepted -c "$commit" -h "$hash" 17 | done 18 | -------------------------------------------------------------------------------- /tools/post-receive.hook: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Git post-receive hook to update Patchwork patches after Git pushes 4 | # 5 | # Copyright © 2010 martin f. krafft 6 | # Released under the GNU General Public License v2 or later. 7 | set -eu 8 | 9 | #TODO: the state map should really live in the repo's git-config 10 | STATE_MAP="refs/heads/master:Accepted" 11 | # 12 | # ignore all commits already present in these refs 13 | # e.g., 14 | # EXCLUDE="refs/heads/upstream refs/heads/other-project" 15 | # 16 | EXCLUDE="" 17 | 18 | PWDIR=/srv/patchwork/patchwork 19 | 20 | do_exit=0 21 | trap "do_exit=1" INT 22 | 23 | get_patchwork_hash() 24 | { 25 | local hash 26 | hash=$(git show -C $1 | python $PWDIR/parser.py --hash) 27 | echo $hash 28 | test -n "$hash" 29 | } 30 | 31 | get_patch_id() 32 | { 33 | local id 34 | id=$($PWDIR/bin/pwclient info -h $1 2>/dev/null \ 35 | | sed -rne 's,- id[[:space:]]*: ,,p') 36 | echo $id 37 | test -n "$id" 38 | } 39 | 40 | set_patch_state() 41 | { 42 | $PWDIR/bin/pwclient update -s $2 -c $3 $1 2>&1 43 | } 44 | 45 | update_patches() 46 | { 47 | local cnt; cnt=0 48 | for rev in $(git rev-parse --not ${EXCLUDE} | 49 | git rev-list --stdin --no-merges --reverse ${1}..${2}); do 50 | if [ "$do_exit" = 1 ]; then 51 | echo "I: exiting..." >&2 52 | break 53 | fi 54 | hash=$(get_patchwork_hash $rev) \ 55 | || { echo "E: failed to hash rev $rev." >&2; continue; } 56 | id=$(get_patch_id $hash) \ 57 | || { echo "E: failed to find patch for rev $rev." >&2; continue; } 58 | reason="$(set_patch_state $id $3 $rev)" \ 59 | || { echo "E: failed to update patch #$id${reason:+: $reason}." >&2; continue; } 60 | echo "I: patch #$id updated using rev $rev." >&2 61 | cnt=$(($cnt + 1)) 62 | done 63 | echo "I: $cnt patch(es) updated to state $3." >&2 64 | } 65 | 66 | while read oldrev newrev refname; do 67 | found=0 68 | for i in $STATE_MAP; do 69 | key="${i%:*}" 70 | if [ "$key" = "$refname" ]; then 71 | update_patches $oldrev $newrev ${i#*:} 72 | found=1 73 | break 74 | fi 75 | done 76 | if [ $found -eq 0 ]; then 77 | echo "E: STATE_MAP has no mapping for branch $refname" >&2 78 | fi 79 | done 80 | -------------------------------------------------------------------------------- /tools/run-devel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Patchwork - automated patch tracking system 3 | # Copyright (C) 2015 Intel Corporation 4 | # 5 | # This file is part of the Patchwork package. 6 | # 7 | # Patchwork is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Patchwork is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Patchwork; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | 21 | ADDRESS=0.0.0.0:8000 22 | CFG=patchwork.settings.dev-sqlite 23 | 24 | cd $(dirname $0)/.. 25 | . venv/bin/activate 26 | export DJANGO_SETTINGS_MODULE=$CFG 27 | ./manage.py runserver ${ADDRESS} 28 | deactivate 29 | -------------------------------------------------------------------------------- /tools/setup-devel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Patchwork - automated patch tracking system 3 | # Copyright (C) 2015 Intel Corporation 4 | # 5 | # This file is part of the Patchwork package. 6 | # 7 | # Patchwork is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Patchwork is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Patchwork; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | 21 | 22 | CFG=patchwork.settings.dev-sqlite 23 | 24 | update_virtualenv() 25 | { 26 | directory=$1 27 | requirements=$2 28 | 29 | [ -d "$directory" ] || virtualenv "$directory" 30 | . $directory/bin/activate 31 | pip install --upgrade -r $requirements 32 | } 33 | 34 | cd $(dirname $0)/.. 35 | 36 | update_virtualenv venv docs/requirements-dev-sqlite.txt 37 | export DJANGO_SETTINGS_MODULE=$CFG 38 | ./manage.py migrate --noinput 39 | ./manage.py loaddata test_data 40 | deactivate 41 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 1.6 3 | envlist = py{27,36}-django111, pep8 4 | skipsdist = True 5 | 6 | [testenv] 7 | deps = 8 | -r{toxinidir}/docs/requirements-dev.txt 9 | mysqlclient>=1.3.0,<1.4 10 | django111: django>=1.11,<1.12 11 | setenv = 12 | DJANGO_SETTINGS_MODULE = patchwork.settings.dev 13 | passenv = 14 | http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY 15 | PW_TEST_DB_HOST PW_TEST_DB_TYPE PW_TEST_DB_USER PW_TEST_DB_PASS 16 | DISPLAY PATCHWORK_SKIP_BROWSER_TESTS SELENIUM_BROWSER CHROME_BIN 17 | commands = 18 | {toxinidir}/manage.py test --noinput '{posargs:patchwork}' 19 | 20 | [testenv:pep8] 21 | basepython = python3.6 22 | deps = flake8 23 | commands = flake8 {posargs:patchwork patchwork/bin/pwclient git-pw/git-pw} 24 | 25 | [flake8] 26 | ignore = E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E203,E241,E251,E402,H405,F405,W504 27 | exclude = ./patchwork/migrations 28 | 29 | [testenv:lint] 30 | basepython = python3.6 31 | deps = 32 | pylint 33 | pylint-django 34 | -r{toxinidir}/docs/requirements-prod-mysql.txt 35 | commands = pylint --rcfile=pylint.rc --load-plugins pylint_django {posargs:patchwork} 36 | 37 | [testenv:venv] 38 | commands = {posargs} 39 | 40 | [testenv:coverage] 41 | basepython = python2.7 42 | deps = 43 | coverage 44 | -r{toxinidir}/docs/requirements-prod-mysql.txt 45 | setenv = 46 | DJANGO_SETTINGS_MODULE = patchwork.settings.dev 47 | commands = 48 | coverage erase 49 | coverage run --omit=*tox*,patchwork/tests/*.py,manage.py --branch \ 50 | {toxinidir}/manage.py test --noinput patchwork 51 | coverage report -m 52 | --------------------------------------------------------------------------------