stable-rt-tools 1.5__tar.gz → 2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. stable_rt_tools-2.0/PKG-INFO +39 -0
  2. stable_rt_tools-2.0/README.rst +6 -0
  3. stable_rt_tools-2.0/pyproject.toml +89 -0
  4. stable_rt_tools-1.5/stable_rt_tools/srt_git_filter.py → stable_rt_tools-2.0/stable_rt_tools/about.py +5 -22
  5. stable_rt_tools-2.0/stable_rt_tools/announce-srt-rc.txt +29 -0
  6. stable_rt_tools-2.0/stable_rt_tools/announce-srt.txt +39 -0
  7. stable_rt_tools-2.0/stable_rt_tools/commit-tmpl.txt +34 -0
  8. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/srt.py +29 -10
  9. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/srt_announce.py +73 -58
  10. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/srt_commit.py +38 -29
  11. stable_rt_tools-2.0/stable_rt_tools/srt_config.py +205 -0
  12. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/srt_create.py +22 -23
  13. stable_rt_tools-2.0/stable_rt_tools/srt_prep.py +154 -0
  14. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/srt_push.py +21 -10
  15. stable_rt_tools-2.0/stable_rt_tools/srt_quilt.py +653 -0
  16. stable_rt_tools-2.0/stable_rt_tools/srt_setup.py +406 -0
  17. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/srt_sign.py +20 -19
  18. stable_rt_tools-1.5/stable_rt_tools/about.py → stable_rt_tools-2.0/stable_rt_tools/srt_status.py +32 -33
  19. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/srt_tag.py +15 -7
  20. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/srt_upload.py +17 -7
  21. stable_rt_tools-2.0/stable_rt_tools/srt_util.py +460 -0
  22. stable_rt_tools-2.0/stable_rt_tools/srt_util_context.py +130 -0
  23. stable_rt_tools-2.0/stable_rt_tools/srt_util_tag.py +140 -0
  24. stable_rt_tools-2.0/stable_rt_tools.egg-info/PKG-INFO +39 -0
  25. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools.egg-info/SOURCES.txt +9 -11
  26. stable_rt_tools-2.0/stable_rt_tools.egg-info/requires.txt +16 -0
  27. stable_rt_tools-1.5/PKG-INFO +0 -37
  28. stable_rt_tools-1.5/README.rst +0 -3
  29. stable_rt_tools-1.5/pyproject.toml +0 -3
  30. stable_rt_tools-1.5/setup.py +0 -94
  31. stable_rt_tools-1.5/stable_rt_tools/srt_patches.py +0 -174
  32. stable_rt_tools-1.5/stable_rt_tools/srt_prep.py +0 -78
  33. stable_rt_tools-1.5/stable_rt_tools/srt_util.py +0 -253
  34. stable_rt_tools-1.5/stable_rt_tools/srt_util_context.py +0 -98
  35. stable_rt_tools-1.5/stable_rt_tools/srt_util_tag.py +0 -121
  36. stable_rt_tools-1.5/stable_rt_tools/tests/test_srt.py +0 -768
  37. stable_rt_tools-1.5/stable_rt_tools/tests/test_srt_announce.py +0 -67
  38. stable_rt_tools-1.5/stable_rt_tools/tests/test_srt_push.py +0 -82
  39. stable_rt_tools-1.5/stable_rt_tools/tests/test_srt_util.py +0 -135
  40. stable_rt_tools-1.5/stable_rt_tools/tests/test_srt_util_context.py +0 -63
  41. stable_rt_tools-1.5/stable_rt_tools/tests/test_srt_util_tag.py +0 -113
  42. stable_rt_tools-1.5/stable_rt_tools.egg-info/PKG-INFO +0 -37
  43. stable_rt_tools-1.5/stable_rt_tools.egg-info/requires.txt +0 -3
  44. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/setup.cfg +0 -0
  45. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools/__init__.py +0 -0
  46. /stable_rt_tools-1.5/stable_rt_tools/tests/__init__.py → /stable_rt_tools-2.0/stable_rt_tools/py.typed +0 -0
  47. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools.egg-info/dependency_links.txt +0 -0
  48. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools.egg-info/entry_points.txt +0 -0
  49. {stable_rt_tools-1.5 → stable_rt_tools-2.0}/stable_rt_tools.egg-info/top_level.txt +0 -0
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: stable-rt-tools
3
+ Version: 2.0
4
+ Summary: Support out of tree patch workflows
5
+ Author-email: Daniel Wagner <dwagner@suse.de>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/igaw/stable-rt-tools
8
+ Project-URL: Documentation, https://stable-rt-tools.readthedocs.io
9
+ Project-URL: Source, https://github.com/igaw/stable-rt-tools.git
10
+ Keywords: Linux,PREEMPT_RT,stable,quilt
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Build Tools
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Requires-Python: >=3.12
20
+ Description-Content-Type: text/x-rst
21
+ Provides-Extra: test
22
+ Requires-Dist: pytest>=8; extra == "test"
23
+ Provides-Extra: lint
24
+ Requires-Dist: ruff>=0.6; extra == "lint"
25
+ Requires-Dist: mypy>=1.13; extra == "lint"
26
+ Provides-Extra: docs
27
+ Requires-Dist: sphinx>=7; extra == "docs"
28
+ Requires-Dist: sphinx-rtd-theme>=2; extra == "docs"
29
+ Requires-Dist: sphinx-argparse>=0.4; extra == "docs"
30
+ Provides-Extra: dev
31
+ Requires-Dist: stable-rt-tools[docs,lint,test]; extra == "dev"
32
+ Requires-Dist: build; extra == "dev"
33
+
34
+ srt maintains out-of-tree patch series on top of upstream releases, e.g.
35
+ on top of the stable kernel releases. It started as the release tool of the
36
+ stable PREEMPT_RT trees (hence the name) and is used for other patch series
37
+ as well.
38
+
39
+ See the `stable-rt-tools documentation <https://stable-rt-tools.readthedocs.io>`_ for further details.
@@ -0,0 +1,6 @@
1
+ srt maintains out-of-tree patch series on top of upstream releases, e.g.
2
+ on top of the stable kernel releases. It started as the release tool of the
3
+ stable PREEMPT_RT trees (hence the name) and is used for other patch series
4
+ as well.
5
+
6
+ See the `stable-rt-tools documentation <https://stable-rt-tools.readthedocs.io>`_ for further details.
@@ -0,0 +1,89 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "stable-rt-tools"
7
+ dynamic = ["version"]
8
+ description = "Support out of tree patch workflows"
9
+ readme = "README.rst"
10
+ license = "MIT"
11
+ requires-python = ">=3.12"
12
+ authors = [{ name = "Daniel Wagner", email = "dwagner@suse.de" }]
13
+ keywords = ["Linux", "PREEMPT_RT", "stable", "quilt"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Intended Audience :: Developers",
17
+ "Topic :: Software Development :: Build Tools",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3 :: Only",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ ]
24
+ dependencies = []
25
+
26
+ [project.optional-dependencies]
27
+ test = ["pytest>=8"]
28
+ lint = ["ruff>=0.6", "mypy>=1.13"]
29
+ docs = ["sphinx>=7", "sphinx-rtd-theme>=2", "sphinx-argparse>=0.4"]
30
+ dev = ["stable-rt-tools[test,lint,docs]", "build"]
31
+
32
+ [project.scripts]
33
+ srt = "stable_rt_tools.srt:main"
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/igaw/stable-rt-tools"
37
+ Documentation = "https://stable-rt-tools.readthedocs.io"
38
+ Source = "https://github.com/igaw/stable-rt-tools.git"
39
+
40
+ [tool.setuptools.dynamic]
41
+ version = { attr = "stable_rt_tools.about.__version__" }
42
+
43
+ [tool.setuptools]
44
+ # only what package-data lists, not everything git or egg-info knows
45
+ include-package-data = false
46
+
47
+ [tool.setuptools.packages.find]
48
+ include = ["stable_rt_tools"]
49
+
50
+ [tool.setuptools.package-data]
51
+ stable_rt_tools = ["*.txt", "py.typed"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["stable_rt_tools/tests"]
55
+ addopts = "-ra"
56
+
57
+ [tool.ruff]
58
+ line-length = 79
59
+
60
+ [tool.ruff.lint]
61
+ select = [
62
+ "E", "W", "F", # pycodestyle, pyflakes
63
+ "B", # bugbear
64
+ "UP", # pyupgrade
65
+ "I", # import order
66
+ "SIM", "C4", # simplifications, comprehensions
67
+ "RUF",
68
+ ]
69
+ ignore = [
70
+ "RUF005", # [*a, b] instead of a + [b]
71
+ "SIM108", # ternary instead of if/else
72
+ ]
73
+
74
+ [tool.ruff.lint.per-file-ignores]
75
+ # nested mock.patch() blocks read better than one long with statement
76
+ "stable_rt_tools/tests/*" = ["SIM117"]
77
+
78
+ [tool.mypy]
79
+ files = ["stable_rt_tools"]
80
+ strict = true
81
+
82
+ [[tool.mypy.overrides]]
83
+ # the tests are checked, but need no annotations
84
+ module = "stable_rt_tools.tests.*"
85
+ disallow_untyped_defs = false
86
+ disallow_incomplete_defs = false
87
+ disallow_untyped_calls = false
88
+ disallow_any_generics = false
89
+ check_untyped_defs = true
@@ -1,8 +1,6 @@
1
- #!/usr/bin/env python3
1
+ # stable-rt-tools - support out of tree patch workflows
2
2
  #
3
- # srt - stable rt tooling
4
- #
5
- # Copyright (c) Siemens AG, 2018
3
+ # Copyright (c) Daniel Wagner <dwagner@suse.de>
6
4
  #
7
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
8
6
  # of this software and associated documentation files (the "Software"), to deal
@@ -20,22 +18,7 @@
20
18
  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
19
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
- # SOFTWARE
24
-
25
-
26
- import os
27
- import sys
28
-
29
- review_tag = os.environ['SRT_REVIEW_TAG']
30
- message = sys.stdin.readlines()
31
-
32
- msg = """\
33
- {} stable review patch.
34
- If anyone has any objections, please let me know.
35
-
36
- -----------
37
- """.format(review_tag)
21
+ # SOFTWARE.
22
+ """The stable-rt-tools version, the single source for pyproject.toml."""
38
23
 
39
- print(message[0])
40
- print(msg)
41
- print(''.join(message[1:]))
24
+ __version__ = '2.0'
@@ -0,0 +1,29 @@
1
+ Dear RT Folks,
2
+
3
+ This is the RT stable review cycle of patch {new_version}.
4
+
5
+ Please scream at me if I messed something up. Please test the patches
6
+ too.
7
+
8
+ The -rc release is also available on kernel.org
9
+
10
+ https://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-stable-rt.git
11
+
12
+ on the v{major}.{minor}-rt-next branch.
13
+
14
+ If all goes well, this patch will be converted to the next main
15
+ release on {release_date}.
16
+
17
+ Signing key fingerprint:
18
+
19
+ {gpg_key_fingerprint}
20
+
21
+ All keys used for the above files and repositories can be found on the
22
+ following git repository:
23
+
24
+ https://git.kernel.org/pub/scm/docs/kernel/pgpkeys.git
25
+
26
+ Enjoy!
27
+ {name}
28
+
29
+ Changes from v{old_version}:
@@ -0,0 +1,39 @@
1
+ From: {sender}
2
+ Subject: [ANNOUNCE] {new_version}
3
+ Date: {date}
4
+ Message-ID: {message_id}
5
+ To: {mail_to}
6
+
7
+ Hello RT-list!
8
+
9
+ I'm pleased to announce the {new_version} stable release.
10
+
11
+ You can get this release via the git tree at:
12
+
13
+ https://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-stable-rt.git
14
+
15
+ branch: {branch_name}
16
+ Head SHA1: {branch_head}
17
+
18
+ Or to build {new_version} directly, the following patches should be applied:
19
+
20
+ https://www.kernel.org/pub/linux/kernel/v{major}.x/linux-{major}.{minor}.tar.xz
21
+
22
+ https://www.kernel.org/pub/linux/kernel/v{major}.x/patch-{major}.{minor}.{patch}.xz
23
+
24
+ https://www.kernel.org{prj_dir}/older/patch-{new_version}.patch.xz
25
+
26
+ Signing key fingerprint:
27
+
28
+ {gpg_key_fingerprint}
29
+
30
+ All keys used for the above files and repositories can be found on the
31
+ following git repository:
32
+
33
+ https://git.kernel.org/pub/scm/docs/kernel/pgpkeys.git
34
+
35
+ Enjoy!
36
+ {name}
37
+
38
+ Changes from v{old_version}:
39
+ ---
@@ -0,0 +1,34 @@
1
+ [ANNOUNCE] {new_version}
2
+
3
+ Hello RT-list!
4
+
5
+ I'm pleased to announce the {new_version} stable release.
6
+
7
+ Changes since {old_version}:
8
+
9
+ {series_changes}
10
+ You can get this release via the git tree at:
11
+
12
+ https://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-stable-rt.git {release_tag}
13
+
14
+ branch: {branch_name}
15
+
16
+ Or to build {new_version} directly, the following patches should be applied:
17
+
18
+ https://www.kernel.org/pub/linux/kernel/v{major}.x/linux-{major}.{minor}.tar.xz
19
+
20
+ https://www.kernel.org/pub/linux/kernel/v{major}.x/patch-{major}.{minor}.{patch}.xz
21
+
22
+ https://www.kernel.org{prj_dir}/older/patch-{new_version}.patch.xz
23
+
24
+ Signing key fingerprint:
25
+
26
+ {gpg_key_fingerprint}
27
+
28
+ All keys used for the above files and repositories can be found on the
29
+ following git repository:
30
+
31
+ https://git.kernel.org/pub/scm/docs/kernel/pgpkeys.git
32
+
33
+ Enjoy!
34
+ {name}
@@ -26,11 +26,24 @@ import argparse
26
26
  import logging
27
27
  import sys
28
28
 
29
- from stable_rt_tools import (srt_announce, srt_commit, srt_create, srt_push,
30
- srt_sign, srt_tag, srt_upload, srt_patches,
31
- srt_prep, about)
29
+ from stable_rt_tools import (
30
+ about,
31
+ srt_announce,
32
+ srt_commit,
33
+ srt_create,
34
+ srt_prep,
35
+ srt_push,
36
+ srt_quilt,
37
+ srt_setup,
38
+ srt_sign,
39
+ srt_status,
40
+ srt_tag,
41
+ srt_upload,
42
+ )
43
+ from stable_rt_tools.srt_config import convert_config_files
32
44
 
33
45
  sub_cmd = {
46
+ 'setup': srt_setup,
34
47
  'prep': srt_prep,
35
48
  'commit': srt_commit,
36
49
  'tag': srt_tag,
@@ -39,12 +52,15 @@ sub_cmd = {
39
52
  'upload': srt_upload,
40
53
  'push': srt_push,
41
54
  'announce': srt_announce,
42
- 'patches': srt_patches,
55
+ 'quilt': srt_quilt,
56
+ 'status': srt_status,
43
57
  }
44
58
 
45
59
 
46
- def srt_get_argparser():
47
- parser = argparse.ArgumentParser(description='srt - stable -rt tool')
60
+ def srt_get_argparser() -> argparse.ArgumentParser:
61
+ parser = argparse.ArgumentParser(
62
+ description='srt - maintain out-of-tree patch series on top of '
63
+ 'upstream releases')
48
64
  parser.add_argument('-d', '--debug',
49
65
  action='store_true',
50
66
  help='Enable debug logging')
@@ -60,11 +76,11 @@ def srt_get_argparser():
60
76
  return parser
61
77
 
62
78
 
63
- def main():
79
+ def main() -> None:
64
80
  parser = srt_get_argparser()
65
81
  args = parser.parse_args(sys.argv[1:])
66
82
  if args.version:
67
- print('stable-rt-tools version {}'.format(about.__version__))
83
+ print(f'stable-rt-tools version {about.__version__}')
68
84
  return
69
85
 
70
86
  if args.debug:
@@ -72,8 +88,11 @@ def main():
72
88
  else:
73
89
  logging.getLogger().setLevel(logging.INFO)
74
90
 
75
- if args.cmd in sub_cmd:
76
- sub_cmd[args.cmd].execute(args)
91
+ if args.cmd not in sub_cmd:
92
+ parser.print_help(sys.stderr)
93
+ sys.exit(2)
94
+ convert_config_files()
95
+ sub_cmd[args.cmd].execute(args)
77
96
 
78
97
 
79
98
  if __name__ == "__main__":
@@ -23,24 +23,28 @@
23
23
  # SOFTWARE
24
24
 
25
25
 
26
- import os
26
+ import argparse
27
+ import re
27
28
  from datetime import date, timedelta
28
29
  from email.utils import make_msgid
29
30
  from time import gmtime, strftime
30
31
 
31
- try:
32
- import importlib.resources as pkg_resources
33
- except ImportError:
34
- import importlib_resources as pkg_resources
35
-
36
- from stable_rt_tools.srt_util import (check_context, cmd, confirm, get_config,
37
- get_gpg_fingerprint,
38
- get_local_branch_name,
39
- get_remote_branch_name)
32
+ from stable_rt_tools.srt_util import (
33
+ Config,
34
+ SubParsers,
35
+ check_context,
36
+ cmd,
37
+ confirm,
38
+ get_config,
39
+ get_gpg_fingerprint,
40
+ get_remote_branch_name,
41
+ get_suffix,
42
+ get_template_path,
43
+ )
40
44
  from stable_rt_tools.srt_util_context import SrtContext
41
45
 
42
46
 
43
- def find_starting_ref(ctx):
47
+ def find_starting_ref(ctx: SrtContext) -> str:
44
48
  # find starting point. first check for stable update only
45
49
  ref = cmd(['git', 'log', '--pretty=format:%H', '--merges', '-n', '1'])
46
50
 
@@ -54,29 +58,36 @@ def find_starting_ref(ctx):
54
58
  return ref
55
59
 
56
60
 
57
- def create_rc_patches(config, ctx):
58
- branch_name = get_local_branch_name()
59
- cmd(['git', 'checkout', '-b', 'next-tmp'])
61
+ REVIEW_TEXT = """\
62
+ {tag} stable review patch.
63
+ If anyone has any objections, please let me know.
60
64
 
61
- srt_env = os.environ.copy()
62
- srt_env['SRT_REVIEW_TAG'] = str(ctx.new_tag)
63
- srt_env['FILTER_BRANCH_SQUELCH_WARNING'] = str(1)
64
- srt_path = os.path.dirname(os.path.realpath(__file__))
65
+ -----------
66
+
67
+ """
65
68
 
66
- ref = find_starting_ref(ctx)
67
- cmd(['git', 'filter-branch', '-f', '--msg-filter',
68
- srt_path + '/srt_git_filter.py', str(ref) + '..'],
69
- env=srt_env)
70
69
 
71
- cmd(['git', 'format-patch', str(ref) + '..',
72
- '-o', ctx.new_dir_mails, '--subject-prefix', 'PATCH RT',
73
- '--cover-letter'])
70
+ def add_review_text(path: str, tag: str) -> None:
71
+ """Put the review note at the top of the body of a patch mail."""
72
+ with open(path) as f:
73
+ headers, sep, body = f.read().partition('\n\n')
74
+ with open(path, 'w') as f:
75
+ f.write(headers + sep + REVIEW_TEXT.format(tag=tag) + body)
74
76
 
75
- cmd(['git', 'checkout', branch_name])
76
- cmd(['git', 'branch', '-D', 'next-tmp'])
77
+
78
+ def create_rc_patches(config: Config, ctx: SrtContext) -> None:
79
+ ref = find_starting_ref(ctx)
80
+ mails = cmd(['git', 'format-patch', f'{ref}..',
81
+ '-o', ctx.new_dir_mails, '--subject-prefix',
82
+ f'PATCH {get_suffix(config).upper()}',
83
+ '--cover-letter']).splitlines()
84
+ for mail in mails:
85
+ if not mail.endswith('/0000-cover-letter.patch'):
86
+ add_review_text(mail, str(ctx.new_tag))
77
87
 
78
88
 
79
- def cover_letter_replacements(config, ctx):
89
+ def cover_letter_replacements(config: Config,
90
+ ctx: SrtContext) -> dict[str, object]:
80
91
  r = {
81
92
  "mail_to": config['MAIL_TO'],
82
93
  "major": ctx.new_tag.major,
@@ -88,7 +99,9 @@ def cover_letter_replacements(config, ctx):
88
99
  "message_id": make_msgid(),
89
100
  "sender": config['SENDER'],
90
101
  "name": config['NAME'],
91
- "new_tag_rt": ctx.new_tag.rt,
102
+ # new_tag_rt: the name templates know from before 2.0
103
+ "new_tag_rt": ctx.new_tag.number,
104
+ "new_tag_number": ctx.new_tag.number,
92
105
  "gpg_key_fingerprint": get_gpg_fingerprint(config),
93
106
  }
94
107
  if ctx.is_rc:
@@ -96,15 +109,15 @@ def cover_letter_replacements(config, ctx):
96
109
  return r
97
110
 
98
111
 
99
- def write_rc_cover_letter(config, ctx):
100
- with open(ctx.new_dir_mails + '/0000-cover-letter.patch', 'r') as f:
112
+ def write_rc_cover_letter(config: Config, ctx: SrtContext) -> None:
113
+ with open(ctx.new_dir_mails + '/0000-cover-letter.patch') as f:
101
114
  coverletter = f.read()
102
115
 
103
116
  coverletter = coverletter.replace('*** SUBJECT HERE ***',
104
117
  'Linux ' + str(ctx.new_tag))
105
118
 
106
119
  rc_text = ''
107
- with open(get_rc_templ_path(config), 'r') as f:
120
+ with open(get_rc_templ_path(config)) as f:
108
121
  rc_text = f.read()
109
122
 
110
123
  r = cover_letter_replacements(config, ctx)
@@ -122,11 +135,13 @@ def write_rc_cover_letter(config, ctx):
122
135
  f.write(coverletter)
123
136
 
124
137
 
125
- def send_rc_patches(config, ctx, args):
138
+ def send_rc_patches(config: Config, ctx: SrtContext,
139
+ args: argparse.Namespace) -> None:
126
140
  gmd = ['git', 'send-email', '--confirm=never']
127
141
  if args.suppress_cc:
128
142
  gmd += ['--suppress-cc=all']
129
- gmd += ['--to="{}"'.format(t) for t in config['MAIL_TO'].split(',')]
143
+ # an argument list, no shell: quotes would end up in the address
144
+ gmd += [f'--to={t.strip()}' for t in config['MAIL_TO'].split(',')]
130
145
  gmd += [ctx.new_dir_mails]
131
146
 
132
147
  print('Dry run')
@@ -137,32 +152,33 @@ def send_rc_patches(config, ctx, args):
137
152
  cmd(gmd)
138
153
 
139
154
 
140
- def announce_rc(config, ctx, args):
155
+ def announce_rc(config: Config, ctx: SrtContext,
156
+ args: argparse.Namespace) -> None:
141
157
  create_rc_patches(config, ctx)
142
158
  write_rc_cover_letter(config, ctx)
143
159
  send_rc_patches(config, ctx, args)
144
160
 
145
161
 
146
- def get_announce_tmpl_path(config):
147
- if 'ANNOUNCE' in config:
148
- return config['ANNOUNCE']
162
+ def get_announce_tmpl_path(config: Config) -> str:
163
+ return get_template_path(config, 'ANNOUNCE', 'announce-srt.txt')
149
164
 
150
- # Use importlib.resources.files for modern resource access
151
- return str(
152
- pkg_resources.files('stable_rt_tools').joinpath('announce-srt.txt')
153
- )
154
165
 
166
+ def get_rc_templ_path(config: Config) -> str:
167
+ return get_template_path(config, 'RC_TEXT', 'announce-srt-rc.txt')
155
168
 
156
- def get_rc_templ_path(config):
157
- if 'RC_TEXT' in config:
158
- return config['RC_TEXT']
159
169
 
160
- return str(
161
- pkg_resources.files('stable_rt_tools').joinpath('announce-srt-rc.txt')
162
- )
170
+ def strip_trailers(msg: str) -> str:
171
+ """Remove the trailer block (Signed-off-by: ...) of a commit message."""
172
+ paragraphs = msg.rstrip().split('\n\n')
173
+ last = paragraphs[-1].splitlines()
174
+ if len(paragraphs) > 1 and all(
175
+ re.match(r'^[A-Za-z][A-Za-z0-9-]*: ', line) for line in last):
176
+ paragraphs = paragraphs[:-1]
177
+ return '\n\n'.join(paragraphs)
163
178
 
164
179
 
165
- def announce(config, ctx, args):
180
+ def announce(config: Config, ctx: SrtContext,
181
+ args: argparse.Namespace) -> None:
166
182
  from stable_rt_tools.srt_util import is_quilt_workflow
167
183
  if ctx.is_rc:
168
184
  announce_rc(config, ctx, args)
@@ -181,6 +197,7 @@ def announce(config, ctx, args):
181
197
  msg = '\n'.join(lines[1:]).lstrip('\n')
182
198
  else:
183
199
  pass
200
+ msg = strip_trailers(msg)
184
201
  timestamp = strftime('%a, %d %b %Y %H:%M:%S -0000', gmtime())
185
202
  sender = config.get('SENDER', '')
186
203
  mail_to = config.get('MAIL_TO', '')
@@ -198,7 +215,7 @@ def announce(config, ctx, args):
198
215
  timestamp = strftime('%a, %d %b %Y %H:%M:%S -0000', gmtime())
199
216
 
200
217
  stable_rt_text = ''
201
- with open(get_announce_tmpl_path(config), 'r') as f:
218
+ with open(get_announce_tmpl_path(config)) as f:
202
219
  stable_rt_text = f.read()
203
220
 
204
221
  r = cover_letter_replacements(config, ctx)
@@ -210,21 +227,19 @@ def announce(config, ctx, args):
210
227
  print(stable_rt_text.format(**r))
211
228
 
212
229
  ref = find_starting_ref(ctx)
213
- print(cmd(['git', '--no-pager', 'shortlog', '{0}..{1}'.
214
- format(ref, ctx.new_tag)]))
230
+ print(cmd(['git', '--no-pager', 'shortlog', f'{ref}..{ctx.new_tag}']))
215
231
 
216
232
  print('---')
217
233
 
218
- print(cmd(['git', '--no-pager', 'diff', '--stat', '{0}..{1}'.
219
- format(ref, ctx.new_tag)]))
234
+ print(cmd(['git', '--no-pager', 'diff', '--stat',
235
+ f'{ref}..{ctx.new_tag}']))
220
236
 
221
237
  print('---')
222
238
 
223
- print(cmd(['git', '--no-pager', 'diff', '{0}..{1}'.
224
- format(ref, ctx.new_tag)]))
239
+ print(cmd(['git', '--no-pager', 'diff', f'{ref}..{ctx.new_tag}']))
225
240
 
226
241
 
227
- def add_argparser(parser):
242
+ def add_argparser(parser: SubParsers) -> argparse.ArgumentParser:
228
243
  prs = parser.add_parser('announce')
229
244
  prs.add_argument('OLD_TAG', nargs='?')
230
245
  prs.add_argument('NEW_TAG', nargs='?')
@@ -233,7 +248,7 @@ def add_argparser(parser):
233
248
  return prs
234
249
 
235
250
 
236
- def execute(args):
251
+ def execute(args: argparse.Namespace) -> None:
237
252
  ctx = SrtContext(args)
238
253
  check_context(ctx)
239
254