Repository: jupyterhub/zero-to-jupyterhub-k8s

Issue:
Dynamically annotate the images used in Chart.yaml for scanning in artifacthub.io
It would be nice if we can dynamically [set artifacthub specific _annotations_ in Chart.yaml](https://artifacthub.io/docs/topics/annotations/helm/) to contain the specific images of the chart. If we do, then those will be scanned by artifacthub.io automatically by aquasecurity/trivy and it looks good for the users to see those scan reports for the chart of the given version etc.

We are actually having automated scans with aquasecurity/trivy already in this repo with automatic PRs patching known vulnerabilities, so we will look good by having this.

The official artifcat-hub helm chart itself demonstrates how it would look [here](https://artifacthub.io/packages/helm/artifact-hub/artifact-hub).

![image](https://user-images.githubusercontent.com/3837114/108455354-6025ef80-726e-11eb-93d4-fae5c37a0e10.png)
![image](https://user-images.githubusercontent.com/3837114/108455371-6a47ee00-726e-11eb-9dc8-bfa35f7e4938.png)



Discussion:

Resolving patch:
diff --git a/tools/set-chart-yaml-annotations.py b/tools/set-chart-yaml-annotations.py
new file mode 100755
--- /dev/null
+++ b/tools/set-chart-yaml-annotations.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+"""
+This script inspects the images used by the chart and updates Chart.yaml's
+annotations. Specifically, an annotation read by artificathub.io. For more
+information, see this issue describing why we want this:
+
+https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/2044
+
+FIXME: This implementation is done quick and dirty by appending to Chart.yaml
+       instead of loading it, updating it, and writing content back.
+"""
+
+import os
+import textwrap
+
+from collections.abc import MutableMapping
+
+import yaml
+
+here_dir = os.path.abspath(os.path.dirname(__file__))
+values_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "values.yaml")
+chart_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "Chart.yaml")
+
+
+def find_images(values, images=None):
+    """
+    Searches through values.yaml for images and their tags and returns a list of
+    "<image>:<tag>" strings.
+    """
+    if images == None:
+        images = []
+
+    for k, v in values.items():
+        if isinstance(v, MutableMapping):
+            if k == "image":
+                if "name" in v and "tag" in v:
+                    images.append(f"{v['name']}:{v['tag']}")
+            else:
+                find_images(v, images)
+    return sorted(images)
+
+
+def run():
+    with open(values_yaml) as f:
+        values = yaml.safe_load(f)
+
+    images = find_images(values)
+
+    images_artifacthub_format = textwrap.indent(
+        yaml.dump(
+            [{"name": i.split(":")[0].split("/")[-1], "image": i} for i in images]
+        ),
+        "    ",
+    )
+    chart_yaml_appendix = (
+        "annotations:\n" + '  "artifacthub.io/images": |\n' + images_artifacthub_format
+    )
+
+    print("Appending the following to Chart.yaml:\n")
+    print(chart_yaml_appendix)
+
+    with open(chart_yaml, "a") as f:
+        f.write(chart_yaml_appendix)
+
+    print("jupyterhub/Chart.yaml annotations updated")
+
+
+run()


Test patch: