public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
To: git-commits@fedoraproject.org
Subject: [rpms/rpkg] 1.70-1: A few patches mostly for `pre-push-check`
Date: Mon, 10 Aug 2026 21:44:34 GMT [thread overview]
Message-ID: <178639827417.1.8973643159217975930.rpms-rpkg-2f3c6a1fcb58@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/rpkg
Branch : 1.70-1
Commit : 2f3c6a1fcb5892acc0800ba14b0798f6bd545979
Author : Ondřej Nosek <onosek@redhat.com>
Date : 2023-04-18T20:11:35+00:00
Stats : +424/-1 in 7 file(s)
URL : https://src.fedoraproject.org/rpms/rpkg/c/2f3c6a1fcb5892acc0800ba14b0798f6bd545979?branch=1.70-1
Log:
A few patches mostly for `pre-push-check`
- Patch: Pre-push hook won't check private branches
- Patch: Config file option to skip the hook script creation
- Patch: Allow empty commits when `uses_rpmautospec`
- Patch: Check remote file with correct hash
- Patch: Ignore missing spec file in pre-push hook
- Patch: import_srpm: allow pre-generated srpms
Signed-off-by: Ondřej Nosek <onosek@redhat.com>
---
diff --git a/0014-import_srpm-allow-pre-generated-srpms.patch b/0014-import_srpm-allow-pre-generated-srpms.patch
new file mode 100644
index 0000000..f596265
--- /dev/null
+++ b/0014-import_srpm-allow-pre-generated-srpms.patch
@@ -0,0 +1,105 @@
+From d87cb37fa2fea2ed535b9085a1f4c607083e1c2e Mon Sep 17 00:00:00 2001
+From: Ondrej Nosek <onosek@redhat.com>
+Date: Tue, 4 Apr 2023 01:40:23 +0200
+Subject: [PATCH 1/6] import_srpm: allow pre-generated srpms
+
+When active, do not care specfile in the srpm is processed by
+rpmautospec. Can be activated only directly via pyrpkg 'Commands'
+object.
+
+Relates: https://github.com/fedora-copr/copr/issues/2317
+Fixes: #655
+RHELCMP-11085
+
+Signed-off-by: Ondrej Nosek <onosek@redhat.com>
+---
+ pyrpkg/__init__.py | 6 +++++-
+ tests/test_cli.py | 41 +++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 46 insertions(+), 1 deletion(-)
+
+diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py
+index 9996402..ecb99c9 100644
+--- a/pyrpkg/__init__.py
++++ b/pyrpkg/__init__.py
+@@ -110,7 +110,7 @@ class Commands(object):
+ build_client, user=None,
+ dist=None, target=None, quiet=False,
+ distgit_namespaced=False, realms=None, lookaside_namespaced=False,
+- git_excludes=None, results_dir='root'):
++ git_excludes=None, results_dir='root', allow_pre_generated_srpm=False):
+ """Init the object and some configuration details."""
+
+ # Path to operate on, most often pwd
+@@ -239,6 +239,9 @@ class Commands(object):
+ # Layout setup
+ self.layout = layout.build(self.path,
+ 'resultsdir' if self.results_dir == 'subdir' else None)
++ # A Configuration value used in 'import_srpm' command (comes from the Copr team)
++ # If pre-generated srpms are allowed, don't care specfile is processed by rpmautospec
++ self.allow_pre_generated_srpm = allow_pre_generated_srpm
+
+ # Define properties here
+ # Properties allow us to "lazy load" various attributes, which also means
+@@ -1471,6 +1474,7 @@ class Commands(object):
+ # the dist-git repo without any specfiles - right after initialization) we are
+ # not able determine which the main specfile is.
+ if file.endswith('.spec') and not file.startswith('.') \
++ and not self.allow_pre_generated_srpm \
+ and spec_file_processed_by_rpmautospec(file, target_dir):
+ raise rpkgError('SRPM was processed by rpmautospec '
+ '(specfile "{}" was analyzed)'.format(file))
+diff --git a/tests/test_cli.py b/tests/test_cli.py
+index 868ad1f..02620ef 100644
+--- a/tests/test_cli.py
++++ b/tests/test_cli.py
+@@ -1784,6 +1784,47 @@ class TestImportSrpm(LookasideCacheMock, CliTestCase):
+ self.assertFilesExist(['package.rpmlintrc'], search_dir=self.chaos_repo)
+ self.assertFilesNotExist(['the_file_is_not_in_reserved.yaml'], search_dir=self.chaos_repo)
+
++ @patch('pyrpkg.spec_file_processed_by_rpmautospec')
++ def test_import_srpm_not_processed_by_rpmautospec(self, rpmautospec_processed):
++ cli_cmd = ['rpkg', '--path', self.chaos_repo, '--name', 'docpkg',
++ 'import', '--skip-diffs', self.srpm_file]
++
++ rpmautospec_processed.return_value = False
++ with patch('sys.argv', new=cli_cmd):
++ cli = self.new_cli()
++ with patch('pyrpkg.lookaside.CGILookasideCache.upload', self.lookasidecache_upload):
++ cli.import_srpm() # no exception should be raised
++ rpmautospec_processed.assert_called_once()
++
++ @patch('pyrpkg.spec_file_processed_by_rpmautospec')
++ def test_import_srpm_processed_by_rpmautospec(self, rpmautospec_processed):
++ cli_cmd = ['rpkg', '--path', self.chaos_repo, '--name', 'docpkg',
++ 'import', '--skip-diffs', self.srpm_file]
++
++ rpmautospec_processed.return_value = True
++ with patch('sys.argv', new=cli_cmd):
++ cli = self.new_cli()
++ with patch('pyrpkg.lookaside.CGILookasideCache.upload', self.lookasidecache_upload):
++ six.assertRaisesRegex(
++ self,
++ rpkgError,
++ 'SRPM was processed by rpmautospec',
++ cli.import_srpm)
++ rpmautospec_processed.assert_called_once()
++
++ @patch('pyrpkg.spec_file_processed_by_rpmautospec')
++ def test_import_srpm_processed_by_rpmautospec_allowed(self, rpmautospec_processed):
++ cli_cmd = ['rpkg', '--path', self.chaos_repo, '--name', 'docpkg',
++ 'import', '--skip-diffs', self.srpm_file]
++
++ rpmautospec_processed.return_value = True
++ with patch('sys.argv', new=cli_cmd):
++ cli = self.new_cli()
++ cli.cmd.allow_pre_generated_srpm = True
++ with patch('pyrpkg.lookaside.CGILookasideCache.upload', self.lookasidecache_upload):
++ cli.import_srpm() # no exception should be raised
++ rpmautospec_processed.assert_not_called()
++
+
+ class TestMockbuild(CliTestCase):
+ """Test mockbuild command"""
+--
+2.39.2
+
diff --git a/0015-Ignore-missing-spec-file-in-pre-push-hook.patch b/0015-Ignore-missing-spec-file-in-pre-push-hook.patch
new file mode 100644
index 0000000..b32833c
--- /dev/null
+++ b/0015-Ignore-missing-spec-file-in-pre-push-hook.patch
@@ -0,0 +1,37 @@
+From 3ebfeae20c74de0ca4b26b22135e1996265ea4ce Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= <lsedlar@redhat.com>
+Date: Wed, 5 Apr 2023 11:41:23 +0200
+Subject: [PATCH 2/6] Ignore missing spec file in pre-push hook
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For modules or containers there will be no spec file, and there is
+nothing to block the push on.
+
+Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
+---
+ pyrpkg/__init__.py | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py
+index ecb99c9..d3a7a1c 100644
+--- a/pyrpkg/__init__.py
++++ b/pyrpkg/__init__.py
+@@ -4471,7 +4471,12 @@ class Commands(object):
+ # Assume, that specfile names are same in the active branch
+ # and in the pushed branch (git checkout f37 && git push origin rawhide)
+ # in this case 'f37' is active branch and 'rawhide' is pushed branch.
+- specfile_path_absolute = os.path.join(self.layout.specdir, self.spec)
++ try:
++ specfile_path_absolute = os.path.join(self.layout.specdir, self.spec)
++ except rpkgError:
++ # No specfile found, nothing to check
++ return
++
+ # convert to relative path
+ specfile_path = os.path.relpath(specfile_path_absolute, start=self.path)
+ cmd = ['spectool', '-l', os.path.join(clone_dir, specfile_path)]
+--
+2.39.2
+
diff --git a/0016-Check-remote-file-with-correct-hash.patch b/0016-Check-remote-file-with-correct-hash.patch
new file mode 100644
index 0000000..9bf9eac
--- /dev/null
+++ b/0016-Check-remote-file-with-correct-hash.patch
@@ -0,0 +1,61 @@
+From 4bd4ab1823a7d4bc218b8057b7f00808fabf7648 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= <lsedlar@redhat.com>
+Date: Wed, 5 Apr 2023 09:34:33 +0200
+Subject: [PATCH 3/6] Check remote file with correct hash
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The configured hashtype doesn't have to actually be used. There can be
+old repos that still use md5.
+
+JIRA: RHELCMP-11508
+Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
+---
+ pyrpkg/__init__.py | 3 ++-
+ pyrpkg/lookaside.py | 5 +++--
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py
+index d3a7a1c..0b9a869 100644
+--- a/pyrpkg/__init__.py
++++ b/pyrpkg/__init__.py
+@@ -4548,7 +4548,8 @@ class Commands(object):
+ file_exists_in_lookaside = self.lookasidecache.remote_file_exists_head(
+ self.ns_repo_name if self.lookaside_namespaced else self.repo_name,
+ filename,
+- hash)
++ hash,
++ hashtype=entry.hashtype)
+ if not file_exists_in_lookaside:
+ self.log.error('Source file (or tarball) \'{}\' wasn\'t uploaded to the lookaside '
+ 'cache. Push operation was cancelled.'.format(filename))
+diff --git a/pyrpkg/lookaside.py b/pyrpkg/lookaside.py
+index ecbf12b..3efcd88 100644
+--- a/pyrpkg/lookaside.py
++++ b/pyrpkg/lookaside.py
+@@ -200,7 +200,7 @@ class CGILookasideCache(object):
+ if not self.file_is_valid(outfile, hash, hashtype=hashtype):
+ raise DownloadError('%s failed checksum' % filename)
+
+- def remote_file_exists_head(self, name, filename, hash):
++ def remote_file_exists_head(self, name, filename, hash, hashtype):
+ """Verify whether a file exists on the lookaside cache.
+ Uses a HTTP HEAD request and doesn't require authentication.
+
+@@ -209,10 +209,11 @@ class CGILookasideCache(object):
+ the server side expects).
+ :param str filename: The name of the file to check for.
+ :param str hash: The known good hash of the file.
++ :param str hashtype: The type of hash
+ """
+
+ urled_file = urllib.parse.quote(filename)
+- url = self.get_download_url(name, urled_file, hash, self.hashtype)
++ url = self.get_download_url(name, urled_file, hash, hashtype or self.hashtype)
+
+ c = pycurl.Curl()
+ c.setopt(pycurl.URL, url)
+--
+2.39.2
+
diff --git a/0017-Allow-empty-commits-when-uses_rpmautospec.patch b/0017-Allow-empty-commits-when-uses_rpmautospec.patch
new file mode 100644
index 0000000..448cbc9
--- /dev/null
+++ b/0017-Allow-empty-commits-when-uses_rpmautospec.patch
@@ -0,0 +1,35 @@
+From d2c63c32306732695b7fe5f9dda3deecb7899f4f Mon Sep 17 00:00:00 2001
+From: Jiri Kyjovsky <j1.kyjovsky@gmail.com>
+Date: Sat, 15 Apr 2023 13:46:21 +0200
+Subject: [PATCH 4/6] Allow empty commits when `uses_rpmautospec`
+
+To avoid situations where the command `commit` by default does
+commit when no change in repo is present.
+
+Fixes: #677
+JIRA: RHELCMP-11489
+Merges: https://pagure.io/rpkg/pull-request/688
+
+Signed-off-by: Jiri Kyjovsky <j1.kyjovsky@gmail.com>
+---
+ pyrpkg/__init__.py | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py
+index 0b9a869..187796e 100644
+--- a/pyrpkg/__init__.py
++++ b/pyrpkg/__init__.py
+@@ -1867,7 +1867,9 @@ class Commands(object):
+
+ # construct the git command
+ # We do this via subprocess because the git module is terrible.
+- cmd = ['git', 'commit', '--allow-empty']
++ cmd = ['git', 'commit']
++ if not self.is_retired() and self.uses_rpmautospec:
++ cmd.append('--allow-empty')
+ if signoff:
+ cmd.append('-s')
+ if self.quiet:
+--
+2.39.2
+
diff --git a/0018-Config-file-option-to-skip-the-hook-script-creation.patch b/0018-Config-file-option-to-skip-the-hook-script-creation.patch
new file mode 100644
index 0000000..fa9aa01
--- /dev/null
+++ b/0018-Config-file-option-to-skip-the-hook-script-creation.patch
@@ -0,0 +1,126 @@
+From b48eb502d330ec7a543805d7f185ea270df75b90 Mon Sep 17 00:00:00 2001
+From: Ondrej Nosek <onosek@redhat.com>
+Date: Wed, 12 Apr 2023 00:42:04 +0200
+Subject: [PATCH 5/6] Config file option to skip the hook script creation
+
+A new option named "skip_hooks" can be added to the config file
+(into the main section). It accepts boolean values and when
+the option is present and set, it prevents creating the pre-push
+hook script during cloning a dist-git repository.
+
+Fixes: https://pagure.io/fedpkg/issue/515
+JIRA: RHELCMP-11491
+
+Signed-off-by: Ondrej Nosek <onosek@redhat.com>
+---
+ pyrpkg/__init__.py | 14 ++++++++++----
+ pyrpkg/cli.py | 13 +++++++++++--
+ 2 files changed, 21 insertions(+), 6 deletions(-)
+
+diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py
+index 187796e..7fddff7 100644
+--- a/pyrpkg/__init__.py
++++ b/pyrpkg/__init__.py
+@@ -1571,7 +1571,7 @@ class Commands(object):
+
+ def clone(self, repo, path=None, branch=None, bare_dir=None,
+ anon=False, target=None, depth=None, extra_args=None,
+- config_path=None):
++ config_path=None, skip_hooks=None):
+ """Clone a repo, optionally check out a specific branch.
+
+ :param str repo: the name of the repository to clone.
+@@ -1589,6 +1589,7 @@ class Commands(object):
+ :param list extra_args: additional arguments that are passed to
+ the clone command.
+ :param str config_path: path to the global config file
++ :param bool skip_hooks: skip creation pre-push hook script
+ """
+
+ if not path:
+@@ -1644,7 +1645,8 @@ class Commands(object):
+
+ if not bare_dir:
+ self._add_git_excludes(os.path.join(path, git_dir))
+- self._add_git_pre_push_hook(os.path.join(path, git_dir), config_path)
++ if not skip_hooks:
++ self._add_git_pre_push_hook(os.path.join(path, git_dir), config_path)
+
+ return
+
+@@ -1660,7 +1662,7 @@ class Commands(object):
+ return repo
+
+ def clone_with_dirs(self, repo, anon=False, target=None, depth=None,
+- extra_args=None, config_path=None):
++ extra_args=None, config_path=None, skip_hooks=None):
+ """Clone a repo old style with subdirs for each branch.
+
+ :param str repo: name of the repository to clone.
+@@ -1673,6 +1675,7 @@ class Commands(object):
+ :param list extra_args: additional arguments that are passed to
+ the clone command.
+ :param str config_path: path to the global config file
++ :param bool skip_hooks: skip creation pre-push hook script
+ """
+
+ self._push_url = None
+@@ -1731,7 +1734,8 @@ class Commands(object):
+
+ # Add excludes
+ self._add_git_excludes(branch_path)
+- self._add_git_pre_push_hook(branch_path, config_path)
++ if not skip_hooks:
++ self._add_git_pre_push_hook(branch_path, config_path)
+ except (git.GitCommandError, OSError) as e:
+ raise rpkgError('Could not locally clone %s from %s: %s'
+ % (branch, repo_path, e))
+@@ -1820,6 +1824,8 @@ class Commands(object):
+ # This file was generated by {0} when cloning the repository.
+ # You can edit it to your liking or delete completely. It will not
+ # be recreated.
++ # Creating this file can be also prevented by adding an option
++ # "skip_hooks = True" into the {0}'s config file; [{0}] section.
+
+ _remote="$1"
+ _url="$2"
+diff --git a/pyrpkg/cli.py b/pyrpkg/cli.py
+index 3d8ce33..a1f3f44 100644
+--- a/pyrpkg/cli.py
++++ b/pyrpkg/cli.py
+@@ -2177,13 +2177,21 @@ class cliClient(object):
+ self.log.warning("Repo name should't contain '.git' suffix. "
+ "Correcting the repo name: '%s'" % repo)
+
++ skip_hooks = None
++ if self.config.has_option(self.name, "skip_hooks"):
++ try:
++ skip_hooks = self.config.getboolean(self.name, "skip_hooks")
++ except ValueError:
++ self.log.error("Error: config file option 'skip_hooks'")
++ raise
+ if self.args.branches:
+ self.cmd.clone_with_dirs(self.args.repo[0],
+ anon=self.args.anonymous,
+ target=self.args.clone_target,
+ depth=self.args.depth,
+ extra_args=self.extra_args,
+- config_path=self.args.config)
++ config_path=self.args.config,
++ skip_hooks=skip_hooks)
+ else:
+ self.cmd.clone(self.args.repo[0],
+ branch=self.args.branch,
+@@ -2191,7 +2199,8 @@ class cliClient(object):
+ target=self.args.clone_target,
+ depth=self.args.depth,
+ extra_args=self.extra_args,
+- config_path=self.args.config)
++ config_path=self.args.config,
++ skip_hooks=skip_hooks)
+
+ def commit(self):
+ if self.args.with_changelog and not self.args.message:
+--
+2.39.2
+
diff --git a/0019-Pre-push-hook-won-t-check-private-branches.patch b/0019-Pre-push-hook-won-t-check-private-branches.patch
new file mode 100644
index 0000000..37a7c52
--- /dev/null
+++ b/0019-Pre-push-hook-won-t-check-private-branches.patch
@@ -0,0 +1,45 @@
+From 4553da364d7d8a974ab0c08834ee0a54320da2cb Mon Sep 17 00:00:00 2001
+From: Ondrej Nosek <onosek@redhat.com>
+Date: Tue, 18 Apr 2023 16:06:43 +0200
+Subject: [PATCH 6/6] Pre-push hook won't check private branches
+
+The pre-push hook script was failing the when user tried to push
+a private branch. It required using the --release argument with
+the pre-push-check command and passing additional arguments into
+the hook script. That was found unreliable and private branches
+won't be checked.
+
+Fixes: #683
+JIRA: RHELCMP-11528
+
+Signed-off-by: Ondrej Nosek <onosek@redhat.com>
+---
+ pyrpkg/__init__.py | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py
+index 7fddff7..3f934d3 100644
+--- a/pyrpkg/__init__.py
++++ b/pyrpkg/__init__.py
+@@ -4489,9 +4489,15 @@ class Commands(object):
+ specfile_path = os.path.relpath(specfile_path_absolute, start=self.path)
+ cmd = ['spectool', '-l', os.path.join(clone_dir, specfile_path)]
+ # extract just '--define' arguments from rpmdefines
+- for opt, val in zip(self.rpmdefines[0::2], self.rpmdefines[1::2]):
+- if opt == '--define':
+- cmd.extend((opt, val))
++ try:
++ for opt, val in zip(self.rpmdefines[0::2], self.rpmdefines[1::2]):
++ if opt == '--define':
++ cmd.extend((opt, val))
++ except rpkgError:
++ # this exception was caused probably by using a private branch
++ self.log.warning('The pre-push script can\'t check private branches. '
++ 'Push operation continues.')
++ return
+ ret, stdout, _ = self._run_command(cmd, cwd=clone_dir,
+ return_text=True, return_stdout=True)
+ if ret != 0:
+--
+2.39.2
+
diff --git a/rpkg.spec b/rpkg.spec
index 290e345..60d99d1 100644
--- a/rpkg.spec
+++ b/rpkg.spec
@@ -1,6 +1,6 @@
Name: rpkg
Version: 1.66
-Release: 5%{?dist}
+Release: 6%{?dist}
Summary: Python library for interacting with rpm+git
License: GPLv2+ and LGPLv2
@@ -47,6 +47,12 @@ Patch10: 0010-pre-push-check-have-to-use-spectool-with-define.patch
Patch11: 0011-A-HEAD-query-into-a-lookaside-cache.patch
Patch12: 0012-pre-push-hook-script-contains-a-user-s-config.patch
Patch13: 0013-Fix-unittests-for-clone-and-pre-push-hook-script.patch
+Patch14: 0014-import_srpm-allow-pre-generated-srpms.patch
+Patch15: 0015-Ignore-missing-spec-file-in-pre-push-hook.patch
+Patch16: 0016-Check-remote-file-with-correct-hash.patch
+Patch17: 0017-Allow-empty-commits-when-uses_rpmautospec.patch
+Patch18: 0018-Config-file-option-to-skip-the-hook-script-creation.patch
+Patch19: 0019-Pre-push-hook-won-t-check-private-branches.patch
%description
Python library for interacting with rpm+git
@@ -263,6 +269,14 @@ example_cli_dir=$RPM_BUILD_ROOT%{_datadir}/%{name}/examples/cli
%changelog
+* Tue Apr 18 2023 Ondřej Nosek <onosek@redhat.com> - 1.66-6
+- Patch: Pre-push hook won't check private branches
+- Patch: Config file option to skip the hook script creation
+- Patch: Allow empty commits when `uses_rpmautospec`
+- Patch: Check remote file with correct hash
+- Patch: Ignore missing spec file in pre-push hook
+- Patch: import_srpm: allow pre-generated srpms
+
* Sat Apr 1 2023 Ondřej Nosek <onosek@redhat.com> - 1.66-5
- Patch: Fix unittests for `clone` and pre-push hook script
- Patch: pre-push hook script contains a user's config
next reply other threads:[~2026-08-10 21:44 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 21:44 [this message]
-- strict thread matches above, loose matches on Subject: below --
2026-08-10 21:44 [rpms/rpkg] 1.70-1: A few patches mostly for `pre-push-check`
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178639827417.1.8973643159217975930.rpms-rpkg-2f3c6a1fcb58@fedoraproject.org \
--to=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox