stable-rt-tools 1.4__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 (46) 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.4/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.4 → stable_rt_tools-2.0}/stable_rt_tools/srt.py +30 -8
  9. stable_rt_tools-2.0/stable_rt_tools/srt_announce.py +255 -0
  10. {stable-rt-tools-1.4 → 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.4 → 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.4 → stable_rt_tools-2.0}/stable_rt_tools/srt_push.py +29 -11
  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.4 → stable_rt_tools-2.0}/stable_rt_tools/srt_sign.py +26 -25
  18. stable-rt-tools-1.4/stable_rt_tools/about.py → stable_rt_tools-2.0/stable_rt_tools/srt_status.py +32 -33
  19. {stable-rt-tools-1.4 → stable_rt_tools-2.0}/stable_rt_tools/srt_tag.py +21 -8
  20. {stable-rt-tools-1.4 → 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.4 → stable_rt_tools-2.0}/stable_rt_tools.egg-info/SOURCES.txt +10 -8
  26. stable_rt_tools-2.0/stable_rt_tools.egg-info/requires.txt +16 -0
  27. stable-rt-tools-1.4/PKG-INFO +0 -27
  28. stable-rt-tools-1.4/README.rst +0 -3
  29. stable-rt-tools-1.4/pyproject.toml +0 -3
  30. stable-rt-tools-1.4/setup.py +0 -93
  31. stable-rt-tools-1.4/stable_rt_tools/srt_announce.py +0 -191
  32. stable-rt-tools-1.4/stable_rt_tools/srt_util.py +0 -218
  33. stable-rt-tools-1.4/stable_rt_tools/srt_util_context.py +0 -95
  34. stable-rt-tools-1.4/stable_rt_tools/srt_util_tag.py +0 -111
  35. stable-rt-tools-1.4/stable_rt_tools/tests/test_srt.py +0 -708
  36. stable-rt-tools-1.4/stable_rt_tools/tests/test_srt_util.py +0 -101
  37. stable-rt-tools-1.4/stable_rt_tools/tests/test_srt_util_context.py +0 -63
  38. stable-rt-tools-1.4/stable_rt_tools/tests/test_srt_util_tag.py +0 -113
  39. stable-rt-tools-1.4/stable_rt_tools.egg-info/PKG-INFO +0 -27
  40. stable-rt-tools-1.4/stable_rt_tools.egg-info/requires.txt +0 -3
  41. {stable-rt-tools-1.4 → stable_rt_tools-2.0}/setup.cfg +0 -0
  42. {stable-rt-tools-1.4 → stable_rt_tools-2.0}/stable_rt_tools/__init__.py +0 -0
  43. /stable-rt-tools-1.4/stable_rt_tools/tests/__init__.py → /stable_rt_tools-2.0/stable_rt_tools/py.typed +0 -0
  44. {stable-rt-tools-1.4 → stable_rt_tools-2.0}/stable_rt_tools.egg-info/dependency_links.txt +0 -0
  45. {stable-rt-tools-1.4 → stable_rt_tools-2.0}/stable_rt_tools.egg-info/entry_points.txt +0 -0
  46. {stable-rt-tools-1.4 → 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,10 +26,25 @@ 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, 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
31
44
 
32
45
  sub_cmd = {
46
+ 'setup': srt_setup,
47
+ 'prep': srt_prep,
33
48
  'commit': srt_commit,
34
49
  'tag': srt_tag,
35
50
  'create': srt_create,
@@ -37,11 +52,15 @@ sub_cmd = {
37
52
  'upload': srt_upload,
38
53
  'push': srt_push,
39
54
  'announce': srt_announce,
55
+ 'quilt': srt_quilt,
56
+ 'status': srt_status,
40
57
  }
41
58
 
42
59
 
43
- def srt_get_argparser():
44
- 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')
45
64
  parser.add_argument('-d', '--debug',
46
65
  action='store_true',
47
66
  help='Enable debug logging')
@@ -57,11 +76,11 @@ def srt_get_argparser():
57
76
  return parser
58
77
 
59
78
 
60
- def main():
79
+ def main() -> None:
61
80
  parser = srt_get_argparser()
62
81
  args = parser.parse_args(sys.argv[1:])
63
82
  if args.version:
64
- print('stable-rt-tools version {}'.format(about.__version__))
83
+ print(f'stable-rt-tools version {about.__version__}')
65
84
  return
66
85
 
67
86
  if args.debug:
@@ -69,8 +88,11 @@ def main():
69
88
  else:
70
89
  logging.getLogger().setLevel(logging.INFO)
71
90
 
72
- if args.cmd in sub_cmd:
73
- 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)
74
96
 
75
97
 
76
98
  if __name__ == "__main__":
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # srt - stable rt tooling
4
+ #
5
+ # Copyright (c) Siemens AG, 2018
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE
24
+
25
+
26
+ import argparse
27
+ import re
28
+ from datetime import date, timedelta
29
+ from email.utils import make_msgid
30
+ from time import gmtime, strftime
31
+
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
+ )
44
+ from stable_rt_tools.srt_util_context import SrtContext
45
+
46
+
47
+ def find_starting_ref(ctx: SrtContext) -> str:
48
+ # find starting point. first check for stable update only
49
+ ref = cmd(['git', 'log', '--pretty=format:%H', '--merges', '-n', '1'])
50
+
51
+ # if patches have been added, we find the old tag and should start there
52
+ # and not the last merge
53
+ commits = cmd(['git', 'log', '--simplify-by-decoration',
54
+ '--oneline', str(ref) + '..'])
55
+ if ' '.join(commits.split('\n')).find(str(ctx.old_short_tag)) != -1:
56
+ ref = str(ctx.old_tag)
57
+
58
+ return ref
59
+
60
+
61
+ REVIEW_TEXT = """\
62
+ {tag} stable review patch.
63
+ If anyone has any objections, please let me know.
64
+
65
+ -----------
66
+
67
+ """
68
+
69
+
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)
76
+
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))
87
+
88
+
89
+ def cover_letter_replacements(config: Config,
90
+ ctx: SrtContext) -> dict[str, object]:
91
+ r = {
92
+ "mail_to": config['MAIL_TO'],
93
+ "major": ctx.new_tag.major,
94
+ "minor": ctx.new_tag.minor,
95
+ "patch": ctx.new_tag.patch,
96
+ "new_version": ctx.new_short_tag,
97
+ "old_version": ctx.old_short_tag,
98
+ "prj_dir": config['PRJ_DIR'],
99
+ "message_id": make_msgid(),
100
+ "sender": config['SENDER'],
101
+ "name": config['NAME'],
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,
105
+ "gpg_key_fingerprint": get_gpg_fingerprint(config),
106
+ }
107
+ if ctx.is_rc:
108
+ r["new_tag_rc"] = ctx.new_tag.rc
109
+ return r
110
+
111
+
112
+ def write_rc_cover_letter(config: Config, ctx: SrtContext) -> None:
113
+ with open(ctx.new_dir_mails + '/0000-cover-letter.patch') as f:
114
+ coverletter = f.read()
115
+
116
+ coverletter = coverletter.replace('*** SUBJECT HERE ***',
117
+ 'Linux ' + str(ctx.new_tag))
118
+
119
+ rc_text = ''
120
+ with open(get_rc_templ_path(config)) as f:
121
+ rc_text = f.read()
122
+
123
+ r = cover_letter_replacements(config, ctx)
124
+
125
+ today = date.today()
126
+ rc_date = today + timedelta(weeks=1)
127
+ r["release_date"] = rc_date
128
+
129
+ rc_text = rc_text.format(**r)
130
+
131
+ coverletter = coverletter.replace('*** BLURB HERE ***',
132
+ rc_text)
133
+
134
+ with open(ctx.new_dir_mails + '/0000-cover-letter.patch', 'w') as f:
135
+ f.write(coverletter)
136
+
137
+
138
+ def send_rc_patches(config: Config, ctx: SrtContext,
139
+ args: argparse.Namespace) -> None:
140
+ gmd = ['git', 'send-email', '--confirm=never']
141
+ if args.suppress_cc:
142
+ gmd += ['--suppress-cc=all']
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(',')]
145
+ gmd += [ctx.new_dir_mails]
146
+
147
+ print('Dry run')
148
+ gmdd = gmd.copy()
149
+ gmdd.insert(2, '--dry-run')
150
+ print(cmd(gmdd, verbose=True))
151
+ if confirm('OK to send patches?'):
152
+ cmd(gmd)
153
+
154
+
155
+ def announce_rc(config: Config, ctx: SrtContext,
156
+ args: argparse.Namespace) -> None:
157
+ create_rc_patches(config, ctx)
158
+ write_rc_cover_letter(config, ctx)
159
+ send_rc_patches(config, ctx, args)
160
+
161
+
162
+ def get_announce_tmpl_path(config: Config) -> str:
163
+ return get_template_path(config, 'ANNOUNCE', 'announce-srt.txt')
164
+
165
+
166
+ def get_rc_templ_path(config: Config) -> str:
167
+ return get_template_path(config, 'RC_TEXT', 'announce-srt-rc.txt')
168
+
169
+
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)
178
+
179
+
180
+ def announce(config: Config, ctx: SrtContext,
181
+ args: argparse.Namespace) -> None:
182
+ from stable_rt_tools.srt_util import is_quilt_workflow
183
+ if ctx.is_rc:
184
+ announce_rc(config, ctx, args)
185
+ return
186
+
187
+ if is_quilt_workflow(config):
188
+ import subprocess
189
+ tag = str(ctx.new_tag) + '-patches'
190
+ try:
191
+ msg = subprocess.check_output([
192
+ 'git', 'show', '-s', '--format=%B', tag
193
+ ], encoding='utf-8').strip()
194
+ announce_start = msg.find('[ANNOUNCE]')
195
+ if announce_start != -1:
196
+ lines = msg[announce_start:].splitlines()
197
+ msg = '\n'.join(lines[1:]).lstrip('\n')
198
+ else:
199
+ pass
200
+ msg = strip_trailers(msg)
201
+ timestamp = strftime('%a, %d %b %Y %H:%M:%S -0000', gmtime())
202
+ sender = config.get('SENDER', '')
203
+ mail_to = config.get('MAIL_TO', '')
204
+ print(f"From: {sender}")
205
+ print(f"To: {mail_to}")
206
+ print(f"Subject: [ANNOUNCE] {ctx.new_tag}")
207
+ print(f"Date: {timestamp}\n")
208
+ print(msg)
209
+ except Exception as e:
210
+ print(f"Error extracting announcement from tag {tag}: {e}")
211
+ return
212
+
213
+ # rfc2822.html
214
+ # 3.3. Date and Time Specification
215
+ timestamp = strftime('%a, %d %b %Y %H:%M:%S -0000', gmtime())
216
+
217
+ stable_rt_text = ''
218
+ with open(get_announce_tmpl_path(config)) as f:
219
+ stable_rt_text = f.read()
220
+
221
+ r = cover_letter_replacements(config, ctx)
222
+
223
+ r["date"] = timestamp
224
+ r["branch_name"] = get_remote_branch_name()
225
+ r["branch_head"] = cmd(['git', 'rev-parse', 'HEAD'])
226
+
227
+ print(stable_rt_text.format(**r))
228
+
229
+ ref = find_starting_ref(ctx)
230
+ print(cmd(['git', '--no-pager', 'shortlog', f'{ref}..{ctx.new_tag}']))
231
+
232
+ print('---')
233
+
234
+ print(cmd(['git', '--no-pager', 'diff', '--stat',
235
+ f'{ref}..{ctx.new_tag}']))
236
+
237
+ print('---')
238
+
239
+ print(cmd(['git', '--no-pager', 'diff', f'{ref}..{ctx.new_tag}']))
240
+
241
+
242
+ def add_argparser(parser: SubParsers) -> argparse.ArgumentParser:
243
+ prs = parser.add_parser('announce')
244
+ prs.add_argument('OLD_TAG', nargs='?')
245
+ prs.add_argument('NEW_TAG', nargs='?')
246
+ prs.add_argument('--suppress-cc', '-s', action="store_true", default=False,
247
+ help='Don''t auto-cc anyone (for testing)')
248
+ return prs
249
+
250
+
251
+ def execute(args: argparse.Namespace) -> None:
252
+ ctx = SrtContext(args)
253
+ check_context(ctx)
254
+
255
+ announce(get_config(), ctx, args)