From f6e3a4e94bb3ee938245d1ba948346fbe22e9154 Mon Sep 17 00:00:00 2001
From: Levente Polyak <anthraxx@archlinux.org>
Date: Thu, 21 Oct 2021 21:04:16 +0200
Subject: [PATCH] feature(keyringctl): use build command to create final
 artifacts

This allows an easy to use cli which invokes the export function to get
the keyring and uses the ownertrust and revoke functions to write all
artifacts into a target directory.
---
 .gitignore |   1 +
 Makefile   |   4 +-
 README.md  |   7 +++
 keyringctl | 133 +++++++++++++----------------------------------------
 4 files changed, 42 insertions(+), 103 deletions(-)

diff --git a/.gitignore b/.gitignore
index eb8805f7..2c7a16ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/build
 *~
 archlinux-keyring-*.tar.gz
 archlinux-keyring-*.tar.gz.sig
diff --git a/Makefile b/Makefile
index a6ddec8c..c8dacacf 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 PREFIX ?= /usr/local
 KEYRING_TARGET_DIR=$(DESTDIR)$(PREFIX)/share/pacman/keyrings/
-KEYRING_FILES=$(wildcard keyring/output/*.gpg) $(wildcard keyring/output/*-revoked) $(wildcard keyring/output/*-trusted)
+KEYRING_FILES=$(wildcard build/*.gpg) $(wildcard build/*-revoked) $(wildcard build/*-trusted)
 
 all: build
 
@@ -11,7 +11,7 @@ lint:
 	mypy --install-types --non-interactive keyringctl
 
 build:
-	./keyringctl -v export-keyring
+	./keyringctl -v build
 
 install:
 	install -vDm 755 $(KEYRING_FILES) -t $(KEYRING_TARGET_DIR)
diff --git a/README.md b/README.md
index 08932eb5..4ff64a95 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,13 @@ from the provided data structure and to install it:
 
 ## Usage
 
+### Build
+
+Build all PGP artifacts (keyring, ownertrust, revoked files) to the build directory
+```bash
+./keyringctl build
+```
+
 ### Import
 
 Import a new packager key by deriving the username from the filename.
diff --git a/keyringctl b/keyringctl
index ccdac264..745028cc 100755
--- a/keyringctl
+++ b/keyringctl
@@ -835,45 +835,6 @@ def convert(
     return target_dir
 
 
-def temp_join_keys(sources: List[Path], temp_dir: Path, force: bool) -> List[Path]:
-    """Temporarily join the key material of a given set of keys in a temporary location and return their paths
-
-    Parameters
-    ----------
-    sources: List[Path]
-        A list of paths below which PGP packets are found
-    temp_dir: Path
-        The temporary directory below which to join PGP keys
-    force: bool
-        Whether to force the joining of files
-    """
-
-    certs: List[Path] = []
-
-    for source_number, source in enumerate(sources):
-        if source.is_dir():
-            for user_number, user_dir in enumerate(sorted(source.iterdir())):
-                if user_dir.is_dir():
-                    for user_cert_number, user_cert_dir in enumerate(sorted(user_dir.iterdir())):
-                        if user_cert_dir.is_dir():
-                            cert_path = temp_dir / (
-                                f"{str(source_number).zfill(4)}"
-                                f"-{str(user_number).zfill(4)}"
-                                f"-{str(user_cert_number).zfill(4)}.asc"
-                            )
-                            debug(f"Joining {user_dir.name}/{user_cert_dir.name} in {cert_path}.")
-                            packet_join(
-                                packets=sorted(user_cert_dir.glob("**/*.asc")),
-                                output=cert_path,
-                                force=force,
-                            )
-                            certs.append(cert_path)
-        elif source.is_file() and not source.is_symlink():
-            certs.append(source)
-
-    return certs
-
-
 def get_all_and_revoked_certs(certs: List[Path]) -> Tuple[List[Fingerprint], List[Fingerprint]]:
     """Get the fingerprints of all public keys and all fingerprints of all (self) revoked public keys in a directory
 
@@ -1131,67 +1092,40 @@ def export(
         )
         certificates.append(cert_path)
 
-    return keyring_merge(certificates, output)
+    return keyring_merge(certificates, output, force=True)
 
 
-def export_keyring(
+def build(
     working_dir: Path,
-    main: List[Path],
-    sources: List[Path],
-    output: Path,
-    force: bool,
-    pacman_integration: bool,
+    keyring_root: Path,
+    target_dir: Path,
 ) -> None:
-    """Export all provided PGP packet files to a single output file
-
-    If sources contains directories, any .asc files below them are considered.
+    """Build keyring PGP artifacts alongside ownertrust and revoked status files
 
     Parameters
     ----------
     working_dir: Path
         A directory to use for temporary files
-    main: List[Path]
-        A list of directories or files from which to read PGP packet information, that is considered as public keys
-        that are used to sign those found in sources
-    sources: List[Path]
-        A list of directories or files from which to read PGP packet information
-    output: Path
-        An output file that all PGP packet data is written to
-    force: bool
-        Whether to force the execution of packet_join()
-    pacman_integration: bool
-        Whether to write pacman compatible trust files
+    keyring_root: Path
+        The keyring root directory to build the artifacts from
+    target_dir: Path
+        Output directory that all artifacts are written to
     """
 
-    main = [source.absolute() for source in main]
-    sources = [source.absolute() for source in sources]
-    output = output.absolute()
+    target_dir.mkdir(parents=True, exist_ok=True)
 
-    main_certs = temp_join_keys(sources=main, temp_dir=Path(mkdtemp(dir=working_dir)).absolute(), force=force)
-    sources_certs = temp_join_keys(sources=sources, temp_dir=Path(mkdtemp(dir=working_dir)).absolute(), force=force)
-    debug(
-        f"Creating keyring {output} from {[str(source_dir) for source_dir in main]} "
-        f"and {[str(source_dir) for source_dir in sources]}."
-    )
+    keyring: Path = target_dir / Path("archlinux.gpg")
+    export(working_dir=working_dir, keyring_root=keyring_root, output=keyring)
 
-    output.parent.mkdir(parents=True, exist_ok=True)
-    cmd = ["sq", "keyring", "merge", "-o", str(output)]
-    if force:
-        cmd.insert(1, "--force")
-    cmd += [str(cert) for cert in sorted(main_certs)]
-    cmd += [str(cert) for cert in sorted(sources_certs)]
-    system(cmd, exit_on_error=False)
-
-    if pacman_integration:
-        [trusted_main_keys, all_main_keys] = export_ownertrust(
-            certs=main,
-            output=Path(f"{str(output).split('.gpg')[0]}-trusted"),
-        )
-        export_revoked(
-            certs=main + sources,
-            main_keys=all_main_keys,
-            output=Path(f"{str(output).split('.gpg')[0]}-revoked"),
-        )
+    [trusted_main_keys, all_main_keys] = export_ownertrust(
+        certs=[keyring_root / "main"],
+        output=target_dir / "archlinux-trusted",
+    )
+    export_revoked(
+        certs=[keyring_root / "main", keyring_root / "packager"],
+        main_keys=all_main_keys,
+        output=target_dir / "archlinux-revoked",
+    )
 
 
 def absolute_path(path: str) -> Path:
@@ -1252,11 +1186,6 @@ if __name__ == "__main__":
     )
     import_parser.add_argument("--main", action="store_true", help="Import a main signing key into the keyring")
 
-    export_keyring_parser = subcommands.add_parser(
-        "export-keyring",
-        help="export PGP packet data below main/ and packager/ to output/archlinux.gpg alongside pacman integration",
-    )
-
     export_parser = subcommands.add_parser(
         "export",
         help="export a directory structure of PGP packet data to a combined file",
@@ -1269,6 +1198,11 @@ if __name__ == "__main__":
         type=absolute_path,
     )
 
+    build_parser = subcommands.add_parser(
+        "build",
+        help="build keyring PGP artifacts alongside ownertrust and revoked status files",
+    )
+
     args = parser.parse_args()
 
     if args.verbose:
@@ -1297,15 +1231,6 @@ if __name__ == "__main__":
                         ),
                     )
                 )
-            elif "export-keyring" == args.subcommand:
-                export_keyring(
-                    working_dir=working_dir,
-                    main=[keyring_root / "main"],
-                    sources=[keyring_root / "packager"],
-                    output=keyring_root / "output" / "archlinux.gpg",
-                    force=True,
-                    pacman_integration=True,
-                )
             elif "export" == args.subcommand:
                 print(
                     export(
@@ -1316,6 +1241,12 @@ if __name__ == "__main__":
                     ),
                     end="",
                 )
+            elif "build" == args.subcommand:
+                build(
+                    working_dir=working_dir,
+                    keyring_root=keyring_root,
+                    target_dir=keyring_root.parent / "build",
+                )
             else:
                 parser.print_help()
 
-- 
GitLab