treewide: build product info

This change adds the type definition and generator for product info,
which will be added to the OCI OS image to provide information about the
contents.

Here is an example product info:

{
	"id": "metropolis-node",
	"name": "Metropolis Node",
	"version": "0.1.0-dev1059",
	"variant": "x86_64-race",
	"commit_hash": "56248c1c1d5039bdf3c1043ade88f3f158ceb52b",
	"commit_date": "2025-05-08T18:26:46+00:00",
	"build_tree_dirty": true,
	"components": [
		{"id": "linux", "version": "6.12.15"},
		{"id": "kubernetes", "version": "1.32.0"}
	]
}

The product info has the same inputs and a similar purpose as the
os-release file, so they are both generated by the same build action.

Change-Id: I89d453f2d72ac9df49e404f46381cd594534f800
Reviewed-on: https://review.monogon.dev/c/monogon/+/4192
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/build/print-workspace-status.py b/build/print-workspace-status.py
index 3b86ee4..34309f3 100755
--- a/build/print-workspace-status.py
+++ b/build/print-workspace-status.py
@@ -63,6 +63,7 @@
 
 if not args.nostamp:
     variables["STABLE_MONOGON_gitCommit"] = git_commit
+    variables["STABLE_MONOGON_gitCommitDate"] = git_commit_date
     variables["STABLE_MONOGON_gitTreeState"] = git_tree_state
 
 if args.nostamp:
@@ -232,6 +233,20 @@
     variables["STABLE_KUBERNETES_gitTreeState"] = ""
     variables["STABLE_KUBERNETES_buildDate"] = "1970-01-01T00:00:00Z"
 
+
+# Collect component versions.
+with open("build/bazel/third_party.MODULE.bazel") as f:
+    third_party_bazel = f.read()
+
+linux_version_result = re.findall(r'^LINUX_VERSION = "([a-zA-Z0-9_.-]+)"$', third_party_bazel, re.MULTILINE)
+if len(linux_version_result) != 1:
+    raise Exception("did not find LINUX_VERSION")
+variables["STABLE_MONOGON_componentVersion_linux"] = linux_version_result[0]
+
+if kubernetes_version[:1] != "v":
+    raise Exception("expected v prefix: " + kubernetes_version)
+variables["STABLE_MONOGON_componentVersion_kubernetes"] = kubernetes_version[1:]
+
 # Emit variables to stdout for consumption by Bazel and targets.
 for key in sorted(variables.keys()):
     print("{} {}".format(key, variables[key]))