diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2019-08-26 20:10:59 +0200 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2019-08-26 20:10:59 +0200 |
commit | f17a0c77ca27298641f4c5d2ca31906d61096df5 (patch) | |
tree | 8e210a38bedbbb6c507c9d7c4441b621a827c7ca | |
download | quilttools-f17a0c77ca27298641f4c5d2ca31906d61096df5.tar.gz |
quilttools: Initial import
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | .gitignore | 24 | ||||
-rw-r--r-- | COPYING | 14 | ||||
-rw-r--r-- | Documentation/Makefile | 19 | ||||
-rw-r--r-- | Documentation/conf.py | 158 | ||||
-rw-r--r-- | Documentation/examples/.mb2q.yaml | 33 | ||||
-rw-r--r-- | Documentation/index.rst | 56 | ||||
-rw-r--r-- | Documentation/installation.rst | 14 | ||||
-rw-r--r-- | Documentation/introduction.rst | 8 | ||||
-rw-r--r-- | Documentation/license-rules.rst | 159 | ||||
-rw-r--r-- | Documentation/man1/mb2q.rst | 166 | ||||
-rw-r--r-- | Documentation/sphinx_static/.gitignore | 0 | ||||
-rw-r--r-- | LICENSES/GPL-2.0 | 266 | ||||
-rw-r--r-- | README.md | 11 | ||||
-rwxr-xr-x | mb2q | 660 |
14 files changed, 1588 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..855ae2b --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Normal rules +*.bz2 +*.diff +*.gz +*.patch +*.orig +*.rej +*.tar +*.xz + +# Editor artifacts +*~ +\#*# + +# Quilt's files +patches +series +.pc/ + +# Python artifacts +*.pyc + +# Sphinx +Documentation/output/ @@ -0,0 +1,14 @@ +mb2q is provided under: + + SPDX-License-Identifier: GPL-2.0-only + +Being under the terms of the GNU General Public License version 2 only, +according with: + + LICENSES/GPL-2.0 + +In addition, other licenses may also apply. Please see: + + Documentation/license-rules.rst + +for more details. diff --git a/Documentation/Makefile b/Documentation/Makefile new file mode 100644 index 0000000..422a271 --- /dev/null +++ b/Documentation/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation generated by sphinx + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = quilttools +SOURCEDIR = . +BUILDDIR = output + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/Documentation/conf.py b/Documentation/conf.py new file mode 100644 index 0000000..962ab51 --- /dev/null +++ b/Documentation/conf.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +# +# quilttools documentation build configuration file, created by +# sphinx-quickstart on Tue May 15 16:12:59 2018. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +import glob + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['sphinx_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'quilttools' +copyright = u'2019, Thomas Gleixner <tglx@linutronix.de>' +author = u'Thomas Gleixner <tglx@linutronix.de>' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'' +# The full version, including alpha/beta/rc tags. +release = u'' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'nature' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['sphinx_static'] + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'quilttoolsdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'quilttools.tex', u'quilttools Documentation', + u'Thomas Gleixner \\textless{}tglx@linutronix.de\\textgreater{}', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + # (master_doc, 'quilttools', u'quilttools Documentation', + # [author], 1), +] + +for f in glob.glob('man1/*.rst'): + src = f.replace('.rst', '') + dst = src.split('/')[1] + man_pages.append((src, dst, u'%s Documentation' % dst, [author], 1)) + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'quilttools', u'quilttools Documentation', + author, 'quilttools', 'Tools for quilt.', + 'Miscellaneous'), +] diff --git a/Documentation/examples/.mb2q.yaml b/Documentation/examples/.mb2q.yaml new file mode 100644 index 0000000..abac225 --- /dev/null +++ b/Documentation/examples/.mb2q.yaml @@ -0,0 +1,33 @@ +# mb2q configuration + +# The committer used for adding the SOB +# If omitted it's retrieved from GIT or environment +committer: Thomas Gleixner <tglx@linutronix.de> + +# List of mail addresses to drop from the CC list +nocc_addrs: + - tglx@linutronix.de + - tglx@tglx.de + +# List of mailing list addresses for which the +# Link: tag is generated +list_addrs: + - linux-kernel@vger.kernel.org + +# List of 'From:' addresses which indicate that the message +# in the mailbox is a mail client internal management mail +# The example below is the magic mail alpine puts into mboxes +# Can be abused to filter other things as well +drop_from: + - '<MAILER-DAEMON@' + +# The Link: base address to which the message ID is appended +link_base: https://lkml.kernel.org/r/ + +# Drop Cc tags from the changelog. They are pointless +# when a link is available. Default is True. Set to False +# to keep them. +dropcc: True + +# Emit committer SOB before Cc and Link tags. +sob_before_cc: True diff --git a/Documentation/index.rst b/Documentation/index.rst new file mode 100644 index 0000000..b6fa628 --- /dev/null +++ b/Documentation/index.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. quilttools documentation master file. + +Welcome to quilttools's documentation! +====================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + +Introduction +------------ + +.. toctree:: + :maxdepth: 2 + + introduction.rst + + +Licensing +--------- + +Describes the licensing rules for the quilttools source code. + +.. toctree:: + :maxdepth: 2 + + license-rules.rst + + +Installation +------------ + +Installation guidelines + +.. toctree:: + :maxdepth: 2 + + installation.rst + + +Usage +----- + +Usage guidelines + +.. toctree:: + :maxdepth: 2 + + man1/mb2q.rst + + +Indices and tables +================== + +* :ref:`genindex` diff --git a/Documentation/installation.rst b/Documentation/installation.rst new file mode 100644 index 0000000..8d5746d --- /dev/null +++ b/Documentation/installation.rst @@ -0,0 +1,14 @@ +.. SPDX-License-Identifier: GPL-2.0 + +quilttools installation guide +============================= + +General +------- + +The recommended installation procedure is via the package management system +of your distribution. + +Manual installation is trivial as the tools are self contained and do not use +libraries. + diff --git a/Documentation/introduction.rst b/Documentation/introduction.rst new file mode 100644 index 0000000..c4e1a2b --- /dev/null +++ b/Documentation/introduction.rst @@ -0,0 +1,8 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Introduction +============ + +quilttools is a collection of python scripts to help with quilt based +workflows. + diff --git a/Documentation/license-rules.rst b/Documentation/license-rules.rst new file mode 100644 index 0000000..33735a8 --- /dev/null +++ b/Documentation/license-rules.rst @@ -0,0 +1,159 @@ +.. SPDX-License-Identifier: GPL-2.0 + +quilttools licensing rules +========================== + +The quilttools project is provided under the terms of the GNU General Public +License version 2 only (GPL-2.0-only), as provided in LICENSES/GPL-2.0. + +This documentation file provides a description of how each source file +should be annotated to make its license clear and unambiguous. +It doesn't replace the projects license. + +The license described in the COPYING file applies to the project source +as a whole, though individual source files can have a different license +which is required to be compatible with the GPL-2.0-only:: + + GPL-2.0-or-later : GNU General Public License v2.0 or later + +Aside from that, individual files can be provided under a dual license, +e.g. one of the compatible GPL variants and alternatively under a +permissive license like BSD, MIT etc. + +The common way of expressing the license of a source file is to add the +matching boilerplate text into the top comment of the file. Due to +formatting, typos etc. these "boilerplates" are hard to validate for +tools which are used in the context of license compliance. + +An alternative to boilerplate text is the use of Software Package Data +Exchange (SPDX) license identifiers in each source file. SPDX license +identifiers are machine parsable and precise shorthands for the license +under which the content of the file is contributed. SPDX license +identifiers are managed by the SPDX Workgroup at the Linux Foundation and +have been agreed on by partners throughout the industry, tool vendors, and +legal teams. For further information see https://spdx.org/ + +The quilttools prokect requires the precise SPDX identifier in all source +files. The valid identifiers used in the quilttools project are explained in +the section `License identifiers`_ and have been retrieved from the +official SPDX license list at https://spdx.org/licenses/ along with the +license texts. + +License identifier syntax +------------------------- + +1. Placement: + + The SPDX license identifier in source files shall be added at the first + possible line in a file which can contain a comment. For the majority + or files this is the first line, except for executable scripts which + require the '#!PATH_TO_INTERPRETER' in the first line. For those + scripts the SPDX identifier goes into the second line. + +| + +2. Style: + + The SPDX license identifier is added in form of a comment. The comment + style depends on the file type:: + + scripts: # SPDX-License-Identifier: <SPDX License Expression> + .py: # SPDX-License-Identifier: <SPDX License Expression> + .rst: .. SPDX-License-Identifier: <SPDX License Expression> + +| + +3. Syntax: + + A <SPDX License Expression> is either an SPDX short form license + identifier found on the SPDX License List. When multiple licenses apply, + an expression consists of keywords "AND", "OR" separating + sub-expressions and surrounded by "(", ")" . + + License identifiers for licenses like [L]GPL with the 'or later' option + are constructed by using a "-or-later" for indicating the 'or later' + option.:: + + # SPDX-License-Identifier: GPL-2.0-or-later + + OR should be used if the file is dual licensed and only one license is + to be selected. For example:: + + # SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause + + AND should be used if the file has multiple licenses whose terms all + apply to use the file. For example, if code is inherited from another + project and permission has been given to put it in the quilttools project, + but the original license terms need to remain in effect:: + + # SPDX-License-Identifier: GPL-2.0-only AND MIT + + +License identifiers +------------------- + +The licenses currently used, as well as the licenses for code added to the +project can be found in the LICENSES directory. + +Whenever possible these licenses should be used as they are known to be +fully compatible and widely used. + +The files in this directory contain the full license text and metatags. +The file names are identical to the SPDX license identifier which shall be +used for the license in source files. + +Examples:: + + LICENSES/GPL-2.0 + +Contains the GPL version 2 license text and the required metatags: + +Metatags: + + The following meta tags must be available in a license file: + + - Valid-License-Identifier: + + One or more lines which declare which License Identifiers are valid + inside the project to reference this particular license text. Usually + this is a single valid identifier, but e.g. for licenses with the 'or + later' options two identifiers are valid. + + - SPDX-URL: + + The URL of the SPDX page which contains additional information related + to the license. + + - Usage-Guidance: + + Freeform text for usage advice. The text must include correct examples + for the SPDX license identifiers as they should be put into source + files according to the `License identifier syntax`_ guidelines. + + - License-Text: + + All text after this tag is treated as the original license text + + File format examples:: + + Valid-License-Identifier: GPL-2.0-only + Valid-License-Identifier: GPL-2.0-or-later + SPDX-URL: https://spdx.org/licenses/GPL-2.0-only.html + Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU General Public License (GPL) version 2 only' use: + SPDX-License-Identifier: GPL-2.0-only + For 'GNU General Public License (GPL) version 2 or any later version' use: + SPDX-License-Identifier: GPL-2.0-or-later + License-Text: + Full license text + +| + +All SPDX license identifiers must have a corresponding file in the LICENSE +subdirectory. This is required to allow tool verification and to have the +licenses ready to read and extract right from the source, which is +recommended by various FOSS organizations, e.g. the `FSFE REUSE initiative +<https://reuse.software/>`_. diff --git a/Documentation/man1/mb2q.rst b/Documentation/man1/mb2q.rst new file mode 100644 index 0000000..5fc5a29 --- /dev/null +++ b/Documentation/man1/mb2q.rst @@ -0,0 +1,166 @@ +.. SPDX-License-Identifier: GPL-2.0 + +mb2q manual page +================ + +Synopsis +-------- + +**mb2q** [*options*] inbox + +Description +----------- + +:program:`mb2q`, A program to create a quilt patch series from a mailbox + +mb2q analyses the complete mailbox and collects various tags (Reviewed-by, +Acked-by, Tested-by) from replies to individual patches and to the cover +letter. If a tag is in a reply to the cover letter it is applied to all +patches which reference the cover letter. + +Optionally the output can be redirected into a mailbox which can be applied +with git-am. + +Options +------- + +positional arguments: + inbox Input mailbox file + +optional arguments: + + -h, --help show this help message and exit + + -c CONFIG, --config CONFIG + Config file. Default: ~/.mb2q.yaml + + -F, --fromnodup Do not duplicate From into the body + By default the From: header is copied + into the mail body (changelog) which is + is convenient if patches which have been + collected from a mailing list are resent. + + A From: tag which is already in the mail + body is preserved and not replaced. + + -m MBOXOUT, --mboxout MBOXOUT + Do not store the result in a quilt series. Generate + a mailbox instead which can be applied with git-am + + -n, --nolink Do not add Link:// tags to patches + + + -N, --noid Do not require Message ID. Implies --nolink. + For testing and in really bad cases. + + -p PATCHESDIR, --patchesdir PATCHESDIR + Use given directory and do not automatically create + patches-$inbox/ + + -a ACKEDBY, --ackedby ACKEDBY + Add Acked-by to all patches. Comma separated list. + "Joe Devel <joe@dev.el>, Dev Joe <dev@joe.org>" + + -r REVIEWEDBY, --reviewedby REVIEWEDBY + Add Reviewed-by to all patches. Comma separated list. + + -t TESTEDBY, --testedby TESTEDBY + Add Tested-by to all patches. Comma separated list + + -C, --CollectCC Collect all CCs from mail header + + +Configuration file +------------------ + +The program has a default configuration built in which can be reconfigured +with a configuration file. The default location is ~/.mb2q.yaml. This can +be overridden by the '-c' command line argument. + +Configuration options +--------------------- + + .. code-block:: yaml + + committer: Surname Name <name@email.addr> + + nocc_addrs: + - nocc@email.addr + - another.nocc@email.addr + + list_addrs: + - linux-kernel@vger.kernel.org + - another-list@listserver.org + + drop_from: + - '<MAILER-DAEMON@' + + link_base: https://lkml.kernel.org/r/ + + dropcc: True + + sob_before_cc: True + +committer: +^^^^^^^^^^ + + Set the committer name/email for the Signed-off-by tag. If the entry is + not in the configuration file mb2q tries to find the committer via git or + the relevant git enviroment variables. + +nocc_addrs: +^^^^^^^^^^^ + + Mail addresses which are always dropped from the Cc list of a mail + independent of the dropcc config option and the '-C' command line + parameter + +list_addrs: +^^^^^^^^^^^ + + A list of mailing list mail addresses which if matches are: + + - a indicator to append a Link tag based on the link_base configuration + entry and the message id of the patch mail + + - removed from the cc list independent of the dropcc config option and + the '-C' command line parameter + +drop_from: +^^^^^^^^^^ + + A list of email addresses which cause a mail in the mailbox to be ignored. + That's useful if the email client which is used to store the mailbox inserts + an administrative email at the beginning of the mailbox. The above example + catches that mail inserted by alpine. + +link_base: +^^^^^^^^^^ + + The base URL for creating Link: tags in the changelog. The Message-ID of + the patch mail is appended to the base URL. + +dropccs: +^^^^^^^^ + + If True which is also the built-in default all Cc's are stripped from the + changelog. If False the Cc's in the changelog of the patch mail are + preserved. The default is True because having all that Cc noise in the + change log is pointless when the original mail can be retrieved via the + Link tag. + +sob_before_cc: +^^^^^^^^^^^^^^ + + It true mb2q emits the committer SOB before Cc and Link tags. Otherwise at + after all tags. + + +See also +-------- + +License +------- +Gnu Public License version 2 + + diff --git a/Documentation/sphinx_static/.gitignore b/Documentation/sphinx_static/.gitignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Documentation/sphinx_static/.gitignore diff --git a/LICENSES/GPL-2.0 b/LICENSES/GPL-2.0 new file mode 100644 index 0000000..0a29508 --- /dev/null +++ b/LICENSES/GPL-2.0 @@ -0,0 +1,266 @@ +Valid-License-Identifier: GPL-2.0-only +Valid-License-Identifier: GPL-2.0-or-later +SPDX-URL: https://spdx.org/licenses/GPL-2.0-only.html +Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU General Public License (GPL) version 2 only' use: + SPDX-License-Identifier: GPL-2.0-only + For 'GNU General Public License (GPL) version 2 or any later version' use: + SPDX-License-Identifier: GPL-2.0-or-later +License-Text: + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to most +of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must make +sure that they, too, receive or can get the source code. And you must show them +these terms so they know their rights. +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish +to avoid the danger that redistributors of a free program will individually +obtain patent licenses, in effect making the program proprietary. To prevent +this, we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included without +limitation in the term "modification".) Each licensee is addressed as "you". +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is not +restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as +you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole + or in part contains or is derived from the Program or any part thereof, + to be licensed as a whole at no charge to all third parties under the + terms of this License. + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use in + the most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a + copy of this License. (Exception: if the Program itself is interactive + but does not normally print such an announcement, your work based on the + Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Program, the distribution of the whole must be on the terms +of this License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all the +source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code distributed +need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component itself +accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of the +rights granted herein. You are not responsible for enforcing compliance by +third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. + +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In such +case, this License incorporates the limitation as if written in the body of +this License. + +9. The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. +Each version is given a distinguishing version number. If the Program specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that +version or of any later version published by the Free Software Foundation. If +the Program does not specify a version number of this License, you may choose +any version ever published by the Free Software Foundation. +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU +ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER +OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fa3a9b --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# mb2q - Mailbox to quilt series converter + +## Install + +It's recommended to generate the appropriate package for your system and +install it via the package manager. + +## Usage + +See Documentation and man pages + @@ -0,0 +1,660 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL2.0 +# Copyright Thomas Gleixner <tglx@linutronix.de> + +from email.utils import make_msgid, formatdate +from argparse import ArgumentParser +import subprocess +import mailbox +import email.policy +import email +import yaml +import time +import sys +import os +import re + +def committer_from_git(): + ''' + Retrieve the commiter name/mail from git or environment + ''' + cmd = "echo $(git config --get user.name)' <'$(git config --get user.email)'>'" + committer = subprocess.getoutput(cmd) + + if committer == " <>": + committer = os.environ.get("GIT_COMMITTER_NAME") + " <" + \ + os.environ.get("GIT_COMMITTER_EMAIL") + ">" + return committer + +''' +Regexes to split out the patch from a mail +''' +re_split_at_patch = [ + re.compile('\ndiff --git a/.* b/.*\n'), + re.compile('\n--- a/.*\n'), + re.compile('\n--- dev/null.*\n'), +] + +def split_at_patch(text): + ''' + Check whether the text has a patch embedded. + If so, split the text and return the parts + If not, return the text as a single entry list + ''' + for r in re_split_at_patch: + m = r.search(text) + if m: + s = m.start() + return [text[:s], text[s+1:]] + return [text, None] + +def get_raw_mailaddr(addr): + ''' + Extract the raw email address from a string + ''' + try: + return addr.split('<')[1].split('>')[0].strip() + except: + return addr + +re_compress_space = re.compile('\s+') + +def decode_hdr(hdr): + ''' + Decode a mail header with encoding + ''' + elm = email.header.decode_header(hdr.strip()) + res = '' + for txt, enc in elm: + # Groan .... + if enc: + res += ' ' + txt.decode(enc) + elif isinstance(txt, str): + res += ' ' + txt + else: + res += ' ' + txt.decode('ascii') + return re_compress_space.sub(' ', res).strip() + +def should_drop(addr, drop): + raw = get_raw_mailaddr(addr) + for d in drop: + if raw == get_raw_mailaddr(d): + return True + return False + +def decode_addrs(hdr, drop=[]): + ''' + Decode mail addresses from a header and handle encondings + ''' + addrs = mailaddrs() + if not hdr: + return addrs + parts = re_compress_space.sub(' ', hdr).split(',') + for p in parts: + addr = decode_hdr(p) + if not should_drop(addr, drop): + addrs.add(addr) + return addrs + +def decode_from(msg): + ''' + Decode the From header and return it as the topmost element + ''' + addrs = decode_addrs(str(msg['From'])) + return addrs.get_first() + +re_noquote = re.compile('[a-zA-Z0-9_\- ]+') + +def encode_hdr(txt): + try: + return txt.encode('ascii').decode() + except: + txt = txt.encode('UTF-8').decode() + +def encode_from(fulladdr): + try: + name, addr = fulladdr.split('<', 1) + name = name.strip() + except: + return fulladdr + + try: + name = txt.encode('ascii').decode() + if not re_noquote.fullmatch(name): + name = '"%s"' %name.replace('"', '') + except: + name = email.header.Header(name).encode() + + return name + ' <' + addr + +class mailaddrs(object): + ''' + Storage for mail addresses with no duplicates allowed + ''' + def __init__(self, addrlist=[]): + self.addrs = {} + self.order = [] + self.append_list(addrlist) + + def add(self, addr_new): + raw = get_raw_mailaddr(addr_new) + if not raw in self.order: + self.order.append(raw) + # Prevent duplicates + addr = self.addrs.get(raw, addr_new) + # Prefer the full Name <addr> variant + if len(addr) < len(addr_new): + addr = addr_new + self.addrs[raw] = addr + + def values(self): + res = [] + for r in self.order: + res.append(self.addrs[r]) + return res + + def extend(self, addrs): + for addr in addrs.values(): + self.add(addr) + + def append_list(self, addrlist): + for addr in addrlist: + if len(addr): + self.add(addr) + + def get_first(self): + return self.addrs[self.order[0]] + + def get_last(self): + return self.addrs[self.order[-1]] + + def remove(self, addr): + raw = get_raw_mailaddr(addr) + if raw in self.order: + del self.addrs[raw] + self.order.remove(raw) + + def contains(self, addrlist): + for addr in addrlist: + raw = get_raw_mailaddr(addr) + if raw in self.order: + return True + return False + +class tagstore(object): + ''' + Storage for links and other things with no duplicates + ''' + def __init__(self): + self.links = [] + + def values(self): + return self.links + + def add(self, link): + if not link in self.links: + self.links.append(link) + +tag_order = [ + 'Fixes', + 'Reported-and-tested-by', + 'Reported-by', + 'Suggested-by', + 'Originally-from', + 'Originally-by', + 'Signed-off-by', + 'Tested-by', + 'Reviewed-by', + 'Acked-by', + 'Cc', + 'Link', +] + +tag_nomail = [ + 'Fixes', + 'Link', +] + +tags_reply = [ + 'Tested-by', + 'Reviewed-by', + 'Acked-by', +] + +re_tags = [] + +def compile_tags(): + for tag in tag_order: + re_tags.append(re.compile('^%s:' %tag, re.IGNORECASE)) + +class patchmsg(object): + ''' + Internal representation of a patch message. + ''' + + re_rmsquare = re.compile('^\[.*?\]') + re_tofname = re.compile('[^0-9a-zA-Z]+') + + def __init__(self, msg, args): + self.args = args + self.tags = {} + + self.date = msg.get('Date', formatdate()) + self.decode_subject(msg) + self.get_references(msg) + self.get_addrs(msg) + self.split_at_patch(msg) + self.scan_tags() + + def decode_subject(self, msg): + subj = msg.get('Subject') + if not subj: + return + # Remove starting brackets stuff + while subj.startswith('['): + subj = self.re_rmsquare.sub('', subj, count=1) + subj = subj.replace('\n', ' ') + re_compress_space.sub(' ', subj) + + # Try to automatically uppercase the start of the sentence + # This assumes that the prefix(es) end with a colon and the + # sentence afterwards does not contain one. + spos = subj.rfind(':') + 1 + if spos > 0 and spos < len(subj): + prefix = subj[:spos] + tail = subj[spos:].strip() + subj = prefix + ' ' + tail[0].capitalize() + tail[1:] + + self.subject = subj.strip() + # Create a filename just in case + self.fname = self.re_tofname.sub('_', self.subject) + + def get_references(self, msg): + self.references = msg.get('References', '').split() + self.inreplyto = msg.get('In-Reply-To', '').split() + self.msgid = msg.get('Message-ID', None) + + def get_addrs(self, msg): + self.author = decode_from(msg) + # Get To and Cc + ccs = decode_addrs(str(msg['To']), drop = self.args.nocc_addrs) + ccs.extend(decode_addrs(str(msg['Cc']), drop = self.args.nocc_addrs)) + + # Check whether the cc list contains a list address match + self.listmatch = ccs.contains(self.args.list_addrs) + # Remove the matching lists from Cc + for addr in self.args.list_addrs: + ccs.remove(addr) + + if self.args.collectcc: + self.tags['Cc'] = ccs + + def split_at_patch(self, msg): + self.text = '' + self.patch = None + + # Handle multipart messages gracefully + # i.e. extract attached patches as well + for part in msg.walk(): + if part.is_multipart(): + continue + # Refuse to handle anything else than plain text + if part.get_content_type() != 'text/plain': + continue + # Get the text with decoding (could be base64 encoded) + text = part.get_payload(decode=True) + if not text: + continue + + text, patch = split_at_patch(text.decode()) + self.text += text + if patch: + self.patch = patch + + def add_tag(self, key, addr, force=False): + + # Drop old links based on linkbase + if key == 'Link' and not force and self.args.droplinks: + if addr.startswith(self.args.linkbase): + return + + # Drop all ccs if requested + if key == 'Cc' and self.args.dropccs: + return + + if key not in tag_nomail: + ts = self.tags.get(key, mailaddrs()) + else: + ts = self.tags.get(key, tagstore()) + ts.add(addr) + self.tags[key] = ts + self.has_tags = True + + def scan_tag(self, txt): + i = 0 + txt = txt.strip() + for r in re_tags: + m = r.search(txt) + if m: + pos = m.end() + tag = tag_order[i] + addr = txt[pos + 1:].strip() + if tag == 'Reported-and-tested-by': + self.add_tag('Reported-by', addr) + tag = 'Tested-by' + self.add_tag(tag, addr) + return True + i += 1 + return False + + def scan_tags(self): + lines = self.text.splitlines() + self.text = '' + self.posttags = '' + self.has_tags = False + self.has_from_in_text = False + discard_seen = False + for l in lines: + if self.scan_tag(l): + continue + + # Drop quoted text, except when it's '>From' + if l.startswith('>') and l.find('From') < 0: + continue + # Respect a From: in the body and update author + if l.startswith('From:'): + self.author = l.split(':')[1] + self.has_from_in_text = True + if l.strip() == '---' : + discard_seen = True + if discard_seen: + self.posttags += l + '\n' + else: + self.text += l + '\n' + + def update_tags(self, pmsg): + for tag in tags_reply: + rtags = pmsg.tags.get(tag, mailaddrs()) + for addr in rtags.values(): + self.add_tag(tag, addr) + + def sanitize_ccs(self): + # If a CC address is in any other tag and remove it + ccs = self.tags.get('Cc', mailaddrs()) + for cc in ccs.values(): + for tag in tag_order: + if tag == 'Cc' or tag in tag_nomail: + continue + if self.tags.get(tag, mailaddrs()).contains([cc]): + ccs.remove(cc) + break + +class quilter(object): + ''' + The worker which turns mbox into quilt + ''' + def __init__(self, args): + # Store args for further reference + self.args = args + + # Extract the acked, tested, reviewedby tags which came from + # the command line + self.tags = { + 'Acked-by' : mailaddrs(args.ackedby.split(',')), + 'Tested-by' : mailaddrs(args.testedby.split(',')), + 'Reviewed-by' : mailaddrs(args.reviewedby.split(',')), + } + + def should_drop(self, msg): + try: + mfrom = str(msg['From']) + except: + mfrom = msg['From'] + for drop in self.args.drop_from: + if mfrom.find(drop) >= 0: + return True + return False + + def check_replies(self, pmsg, ref): + for r in self.replies: + if ref: + which = r.references + else: + which = r.inreplyto + + if pmsg.msgid in which: + pmsg.update_tags(r) + self.replies.remove(r) + + def scan_mbox(self, mbox): + self.patches = [] + self.replies = [] + self.others = [] + + # Initial scan to gather information + for k, msg in mbox.iteritems(): + + if self.should_drop(msg): + continue + if not msg.get('Message-ID') and not self.args.noid: + sys.stderr.write('Drop Message w/o Message-ID: %s\n' + %msg.get('Subject','No subject')) + continue + pmsg = patchmsg(msg, self.args) + + # Found a patch + if pmsg.patch: + # Add the command line supplied tags + pmsg.update_tags(self) + if pmsg.msgid and pmsg.listmatch and not self.args.nolink: + mid = pmsg.msgid.lstrip('<').rstrip('>') + pmsg.add_tag('Link', self.args.linkbase + mid, force = True) + self.patches.append(pmsg) + elif pmsg.has_tags: + self.replies.append(pmsg) + else: + self.others.append(pmsg) + + # Find all direct replies to patches + for pmsg in self.patches: + self.check_replies(pmsg, False) + + # Find all replies which reference a patch + for pmsg in self.patches: + self.check_replies(pmsg, True) + + # Find all direct replies to others (cover letter etc) + for pmsg in self.others: + self.check_replies(pmsg, False) + + # Find all replies which reference others (cover letter etc) + for pmsg in self.others: + self.check_replies(pmsg, True) + + # Sort out others which are not referenced by patches + cover = [] + for o in self.others: + for p in self.patches: + if o.msgid == p.inreplyto or o.msgid in p.references: + cover.append(o) + break + + # Update patches which reference a cover letter + for c in cover: + for p in self.patches: + if o.msgid == p.inreplyto or o.msgid in p.references: + p.update_tags(c) + + # Sanitize Ccs: + for p in self.patches: + p.sanitize_ccs() + + def write_series(self, pdir): + # Write the series file + with open(os.path.join(pdir, 'series'), 'a') as fd: + for pmsg in self.patches: + fname = pmsg.fname + '.patch' + i = 0 + # If the patch file exists, create a new one + # Duplicate subjects happen .... + while os.path.isfile(os.path.join(pdir, fname)): + i += 1 + fname = pmsg.fname + '-%d.patch' % i + pmsg.fname = fname + fd.write(fname + '\n') + + def needs_sob(self, pmsg): + last = pmsg.tags.get('Signed-off-by', mailaddrs()).get_last() + if not last: + return True + return get_raw_mailaddr(self.args.committer) != get_raw_mailaddr(last) + + def create_patch(self, pmsg): + res = '' + if not self.args.fromnodup and not pmsg.has_from_in_text: + res += 'From: %s\n\n' %pmsg.author + + # Drop extra newlines before tags + res += pmsg.text.rstrip() + '\n\n' + + for tag in tag_order: + # If SOB is empty warn + if tag == 'Signed-off-by' and not tag in pmsg.tags: + sys.stderr.write('Patch %s has no SOB\n' %pmsg.subject) + + # Write out the committer SOB before Cc, but not if the + # committer is the author and no other SOBs are behind his + # SOB + if self.args.sob_before_cc and tag == 'Cc' and self.needs_sob(pmsg): + res += 'Signed-off-by: %s\n' %self.args.committer + + if not tag in pmsg.tags: + continue + + for val in pmsg.tags[tag].values(): + res += '%s: %s\n' %(tag, val) + + if not self.args.sob_before_cc and self.needs_sob(pmsg): + res += 'Signed-off-by: %s\n' %self.args.committer + + res += '\n' + res += pmsg.posttags + res += '\n' + res += pmsg.patch + return res + + def write_patch(self, pmsg, pdir): + with open(os.path.join(pdir, pmsg.fname), 'w') as fd: + res = 'From: %s\n' %pmsg.author + res += 'Subject: %s\n' %pmsg.subject + res += 'Date: %s\n\n' %pmsg.date + res += self.create_patch(pmsg) + fd.write(res) + + def write_patches(self, pdir): + for pmsg in self.patches: + self.write_patch(pmsg, pdir) + + def create_mbox(self, fpath): + mbox = mailbox.mbox(fpath, create = True) + for pmsg in self.patches: + pol = email.policy.EmailPolicy(utf8=True) + msg = email.message.EmailMessage(pol) + msg['From'] = email.header.Header(pmsg.author) + msg['Date'] = pmsg.date + msg['Subject'] = encode_hdr(pmsg.subject) + msg['MIME-Version'] = '1.0' + msg['Content-Type'] = 'text/plain' + msg.set_param('charset', 'utf-8', header='Content-Type') + msg['Content-Transfer-Encoding'] = '8bit' + msg['Content-Disposition'] = 'inline' + msg.set_unixfrom('From mb2q ' + time.ctime(time.time())) + msg.set_content(self.create_patch(pmsg)) + mbox.add(mailbox.mboxMessage(msg)) + mbox.close() + +if __name__ == '__main__': + + parser = ArgumentParser(description='Mailbox 2 quilt converter') + parser.add_argument('inbox', metavar='inbox', help='Input mbox file') + parser.add_argument('-c', '--config', dest='config', default='~/.mb2q.yaml', + help='Config file. Default: ~/.mb2q.yaml') + parser.add_argument('-d', '--droplinks', dest='droplinks', + action='store_true', + help='Drop existing Link:// tags in patches') + parser.add_argument('-F', '--fromnodup', dest='fromnodup', + action='store_true', + help='Do not duplicate From into the body') + parser.add_argument('-m', '--mboxout', dest='mboxout', + help='Store result in mailbox and do not quilt') + parser.add_argument('-n', '--nolink', dest='nolink', + action='store_true', + help='Do not add Link:// tags to patches') + parser.add_argument('-N', '--noid', dest='noid', + action='store_true', + help='Do not require Message ID. Implies --nolink.') + parser.add_argument('-p', '--patchesdir', dest='patchesdir', + default=None, + help=''' + Use given directory and do not automatically create + patches-$inbox/''') + parser.add_argument('-a', '--ackedby', dest='ackedby', type=str, + default='', + help=''' + Add Acked-by to all patches. Comma separated list. + "Joe Devel <joe@dev.el>, Dev Joe <dev@joe.org>" + ''') + parser.add_argument('-r', '--reviewedby', dest='reviewedby', type=str, + default='', + help='Add Reviewed-by to all patches') + parser.add_argument('-t', '--testedby', dest='testedby', type=str, + default='', + help='Add Tested-by to all patches') + parser.add_argument('-C', '--CollectCC', dest='collectcc', + action='store_true', + help='Collect all CCs from mail header') + args = parser.parse_args() + + try: + cfg = yaml.load(open(os.path.expanduser(args.config))) + except: + cfg = {} + + # Get committer from config or GIT + args.committer = cfg.get('committer', committer_from_git()) + # Cc's which should be dropped + args.nocc_addrs = cfg.get('nocc_addrs', []) + # Mailing list addresses + args.list_addrs = cfg.get('list_addrs', ['linux-kernel.@vger.kernel.org']) + # Drop mails which originate from a mail clients management mail + # usually the first mail in a mbox + args.drop_from = cfg.get('drop_from', ['<MAILER-DAEMON@']) + # The base address for generating Link:// + args.linkbase = cfg.get('link_base', 'https://lkml.kernel.org/r/') + # Drop all ccs? + args.dropccs = cfg.get('dropccs', True) + # Committer SOB before Cc and Link + args.sob_before_cc = cfg.get('sob_before_cc', True) + + compile_tags() + + q = quilter(args) + mbox = mailbox.mbox(args.inbox, create=False) + q.scan_mbox(mbox) + + if not len(q.patches): + sys.stderr.write("No patches found in mbox\n") + sys.exit(1) + + if args.mboxout: + q.create_mbox(args.mboxout) + else: + # Create the patch directory + if args.patchesdir: + pdir = os.path.expanduser(args.patchesdir) + else: + pdir = 'patches-%s' % os.path.basename(args.inbox) + if not os.path.isdir(pdir): + os.makedirs(pdir) + q.write_series(pdir) + q.write_patches(pdir) |