From c79c7db3e8ec568a45afdc505fbf32e9096b28e8 Mon Sep 17 00:00:00 2001
From: Tom Prince <tom.prince@private.storage>
Date: Mon, 13 Sep 2021 20:36:54 -0600
Subject: [PATCH] Add zkap-spending service package, and tool to update pins.

---
 docs/source/dev/README.rst                   | 14 +++
 nixos/pkgs/default.nix                       |  1 +
 nixos/pkgs/zkap-spending-service/default.nix | 12 +++
 nixos/pkgs/zkap-spending-service/repo.json   |  9 ++
 tools/default.nix                            |  1 +
 tools/update-gitlab-repo                     | 89 ++++++++++++++++++++
 6 files changed, 126 insertions(+)
 create mode 100644 nixos/pkgs/zkap-spending-service/default.nix
 create mode 100644 nixos/pkgs/zkap-spending-service/repo.json
 create mode 100755 tools/update-gitlab-repo

diff --git a/docs/source/dev/README.rst b/docs/source/dev/README.rst
index 14d2de31..1a617e0b 100644
--- a/docs/source/dev/README.rst
+++ b/docs/source/dev/README.rst
@@ -57,6 +57,20 @@ To update the channel, the script will need to be updated,
 along with the filenames that have the channel in them.
 
 
+Gitlab Repositories
+```````````````````
+To update the version of packages we import from gitlab, run:
+
+.. code: shell
+
+   nix-shell --command 'tools/update-gitlab nixos/pkgs/<package>/repo.json'
+
+That will update the package to point at the latest version of the project.\
+The command uses branch and repository owner specified in the ``repo.json`` file,
+but you can override them by passing the ``--branch`` or ``-owner`` arguments to the command.
+A specific revision can also be pinned, by passing ``-rev``.
+
+
 Architecture overview
 ---------------------
 
diff --git a/nixos/pkgs/default.nix b/nixos/pkgs/default.nix
index 3d534430..efcff083 100644
--- a/nixos/pkgs/default.nix
+++ b/nixos/pkgs/default.nix
@@ -20,5 +20,6 @@ let
 in
 {
   zkapissuer = callPackage ./zkapissuer {};
+  zkap-spending-service = callPackage ./zkap-spending-service {};
   inherit (ourpkgs) privatestorage leasereport;
 }
diff --git a/nixos/pkgs/zkap-spending-service/default.nix b/nixos/pkgs/zkap-spending-service/default.nix
new file mode 100644
index 00000000..4716109e
--- /dev/null
+++ b/nixos/pkgs/zkap-spending-service/default.nix
@@ -0,0 +1,12 @@
+{ callPackage, fetchFromGitLab, lib }:
+let
+  repo-data = lib.importJSON ./repo.json;
+
+  repo = fetchFromGitLab (builtins.removeAttrs repo-data [ "branch" ]);
+in
+# We want to check the revision the service reports against the revsion
+# that we install. The upsream derivation doesn't currently know its own
+# version, but we do have it here. Thus, we add it as a meta attribute
+# to the derviation provided from upstream.
+lib.addMetaAttrs { inherit (repo-data) rev; }
+  (callPackage repo {})
diff --git a/nixos/pkgs/zkap-spending-service/repo.json b/nixos/pkgs/zkap-spending-service/repo.json
new file mode 100644
index 00000000..39aeb840
--- /dev/null
+++ b/nixos/pkgs/zkap-spending-service/repo.json
@@ -0,0 +1,9 @@
+{
+  "owner": "privatestorage",
+  "repo": "zkap-spending-service",
+  "rev": "e0d63b79213d16f2de6629167ea8f1236ba22e14",
+  "branch": "main",
+  "domain": "whetstone.privatestorage.io",
+  "outputHash": "30abb0g9xxn4lp493kj5wmz8kj5q2iqvw40m8llqvb3zamx60gd8cy451ii7z15qbrbx9xmjdfw0k4gviij46fkx1s8nbich5c8qx57",
+  "outputHashAlgo": "sha512"
+}
diff --git a/tools/default.nix b/tools/default.nix
index f9a0b1ff..fb44c660 100644
--- a/tools/default.nix
+++ b/tools/default.nix
@@ -15,6 +15,7 @@ let
   };
   python-commands = [
     ./update-nixpkgs
+    ./update-gitlab-repo
   ];
 in
   # This derivation creates a package that wraps our tools to setup an environment
diff --git a/tools/update-gitlab-repo b/tools/update-gitlab-repo
new file mode 100755
index 00000000..ddc82cb7
--- /dev/null
+++ b/tools/update-gitlab-repo
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+
+"""
+Update a pinned gitlab repository.
+
+Pass this path to a JSON file and it will update it to the latest
+version of the branch it specifies. You can also pass a different
+branch or repository owner, which will update the file to point at
+the new branch/repository, and update to the latest version.
+"""
+
+import argparse
+import json
+from pathlib import Path
+
+import httpx
+from ps_tools import get_url_hash
+
+HASH_TYPE = "sha512"
+
+ARCHIVE_TEMPLATE = "https://{domain}/api/v4/projects/{owner}%2F{repo}/repository/archive.tar.gz?sha={rev}"
+BRANCH_TEMPLATE = (
+    "https://{domain}/api/v4/projects/{owner}%2F{repo}/repository/branches/{branch}"
+)
+
+
+def get_gitlab_commit(config):
+    response = httpx.get(BRANCH_TEMPLATE.format(**config))
+    response.raise_for_status()
+    return response.json()["commit"]["id"]
+
+
+def get_gitlab_archive_url(config):
+    return ARCHIVE_TEMPLATE.format(**config)
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument(
+        "repo_file",
+        metavar="repo-file",
+        type=Path,
+        help="JSON file with pinned configuration.",
+    )
+    parser.add_argument(
+        "--branch",
+        type=str,
+        help="Branch to update to.",
+    )
+    parser.add_argument(
+        "--owner",
+        type=str,
+        help="Repository owner to update to.",
+    )
+    parser.add_argument(
+        "--rev",
+        type=str,
+        help="Revision to pin.",
+    )
+    parser.add_argument(
+        "--dry-run",
+        action="store_true",
+    )
+    args = parser.parse_args()
+
+    repo_file = args.repo_file
+    config = json.loads(repo_file.read_text())
+
+    for key in ["owner", "branch"]:
+        if getattr(args, key) is not None:
+            config[key] = getattr(args, key)
+
+    if args.rev is not None:
+        config["rev"] = args.rev
+    else:
+        config["rev"] = get_gitlab_commit(config)
+
+    archive_url = get_gitlab_archive_url(config)
+    config.update(get_url_hash(HASH_TYPE, "source", archive_url))
+
+    output = json.dumps(config, indent=2)
+    if args.dry_run:
+        print(output)
+    else:
+        repo_file.write_text(output)
+
+
+if __name__ == "__main__":
+    main()
-- 
GitLab