From fa4ef72f8b95aba00b106a82598608debb8fa449 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Sun, 19 Sep 2021 09:54:01 -0400 Subject: [PATCH] add backend route to retrieve cgit-style git notes as PGP sig For any available tarball {gitref}.tar.gz, attempt to see if there is a `git notes` attachment in: refs/signatures/tar -> {gitref}.tar.asc refs/signatures/tar.gz -> {gitref}.tar.gz.asc cgit supports this for any of its configurable archive compression formats, and additionally always supports delivering the signature for an uncompressed tarball. This is useful in at least two cases: - Some people may mirror to, or migrate between, multiple forges, including cgit with lots of compression formats enabled. One notes attachment then works for all formats. - git-archive guarantees to be a fully stable tarball output format, but the compressor program it pipes to may not be, depending on implementation (GNU gzip is, busybox gzip used to not be but now is, zstd dedicatedly breaks output reproducibility on every release). Some people rely on this, some people don't. Since sourcehut only supports tar.gz, only tar.gz-compatible signatures are implemented here. Implements https://todo.sr.ht/~sircmpwn/git.sr.ht/231 --- gitsrht/blueprints/repo.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/gitsrht/blueprints/repo.py b/gitsrht/blueprints/repo.py index a043c02..9fb601e 100644 --- a/gitsrht/blueprints/repo.py +++ b/gitsrht/blueprints/repo.py @@ -187,6 +187,27 @@ def lookup_ref(git_repo, ref, path): abort(404) return commit, ref, "/".join(path) +def lookup_signature(git_repo, ref, fmt=None): + commit_or_tag = git_repo.revparse_single(ref) + if not isinstance(commit_or_tag, (pygit2.Commit, pygit2.Tag)): + return None, None + + fmts = ['tar.gz', 'tar'] + + if fmt is not None: + if fmt not in fmts: + return None, None + fmts = [fmt] + + for trial in fmts: + try: + note = git_repo.lookup_note(commit_or_tag.hex, f'refs/notes/signatures/{trial}') + except KeyError: + continue + + return note.message, trial + return None, None + @repo.route("///tree", defaults={"ref": None, "path": ""}) @repo.route("///tree/", defaults={"path": ""}) @repo.route("///tree//item/") @@ -365,6 +386,17 @@ def archive(owner, repo, ref): return send_file(subp.stdout, mimetype="application/tar+gzip", as_attachment=True, attachment_filename=f"{repo.name}-{refname}.tar.gz") +@repo.route("///archive/..asc") +def archivesig(owner, repo, ref, fmt): + owner, repo = get_repo_or_redir(owner, repo) + with GitRepository(repo.path) as git_repo: + sigdata, _ = lookup_signature(git_repo, ref, fmt) + if sigdata is None: + abort(404) + + return send_file(BytesIO(sigdata.encode('utf-8')), mimetype="application/pgp-signature", + as_attachment=True, attachment_filename=f"{repo.name}-{ref}.{fmt}.asc") + class _AnnotatedRef: def __init__(self, repo, ref): self.ref = ref -- 2.38.4