Add pipeline to generate docs (#12)

diff --git a/.github/workflows/ci.sh b/.github/workflows/ci.sh
new file mode 100755
index 0000000..ba8c2db
--- /dev/null
+++ b/.github/workflows/ci.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+set -o pipefail
+
+bazel test //...
+readonly exit_code="$?"
+
+case "${exit_code}" in
+  "4")
+    # Status code indicates that the build succeeded but there were no tests to
+    # run. Ignore and exit successfully.
+    exit "0"
+    ;;
+
+  *)
+    exit "${exit_code}"
+    ;;
+esac
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 13b3233..d2d27c6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,11 +12,16 @@
   contents: read
 
 jobs:
-  build:
+  checks:
     strategy:
       fail-fast: false
       matrix:
         include:
+          - name: "Docs"
+            bazel_version: "rolling"
+            package: "docs"
+            runner: "ubuntu-latest"
+
           - name: "Bazel latest: @package_metadata (Linux x64)"
             bazel_version: "rolling"
             package: "metadata"
@@ -38,11 +43,8 @@
     steps:
       - uses: "actions/checkout@v4"
 
-      - name: "Build"
+      - name: "Run (Bash)"
         working-directory: "${{ github.workspace }}/${{ matrix.package }}"
         env:
           USE_BAZEL_VERSION: "${{ matrix.bazel_version }}"
-        run: |
-          bazel build //...
-
-      # TODO(yannic): Run tests once there are tests.
+        run: bash "${{ github.workspace }}/.github/workflows/ci.sh"
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..84aaf44
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,16 @@
+# Bazel convenience symlinks.
+/bazel-*
+
+# bzlmod
+/MODULE.bazel.lock
+
+# VSCode
+/.vscode
+
+# JetBrains IDEs-
+/.ijwb
+/.clwb
+/.idea
+
+# macOS-specific excludes
+.DS_Store
diff --git a/docs/BUILD b/docs/BUILD
new file mode 100644
index 0000000..b84c046
--- /dev/null
+++ b/docs/BUILD
@@ -0,0 +1,6 @@
+load("@package_metadata//rules:package_metadata.bzl", "package_metadata")
+
+package_metadata(
+    name = "package_metadata",
+    purl = "pkg:github/bazel-contrib/supply-chain@HEAD#docs",
+)
diff --git a/docs/MODULE.bazel b/docs/MODULE.bazel
new file mode 100644
index 0000000..7081418
--- /dev/null
+++ b/docs/MODULE.bazel
@@ -0,0 +1,32 @@
+module(
+    name = "supply-chain-docs",
+    version = "HEAD",
+)
+
+bazel_dep(
+    name = "bazel_skylib",
+    version = "1.7.1",
+)
+bazel_dep(
+    name = "stardoc",
+    version = "0.8.0",
+)
+
+#
+# Modules from this repository.
+#
+
+bazel_dep(
+    name = "package_metadata",
+    # Always overridden to use local path.
+    version = "HEAD",
+)
+
+#
+# Overrides from modules from this repository.
+#
+
+local_path_override(
+    module_name = "package_metadata",
+    path = "../metadata",
+)
diff --git a/docs/metadata/BUILD b/docs/metadata/BUILD
new file mode 100644
index 0000000..eb741fb
--- /dev/null
+++ b/docs/metadata/BUILD
@@ -0,0 +1,17 @@
+load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
+load("@stardoc//stardoc:stardoc.bzl", "stardoc")
+
+stardoc(
+    name = "defs",
+    out = "defs.generated.md",
+    input = "@package_metadata//:defs.bzl",
+    deps = [
+        "@package_metadata//:srcs",
+    ],
+)
+
+diff_test(
+    name = "defs_test",
+    file1 = ":defs",
+    file2 = "defs.md",
+)
diff --git a/docs/metadata/defs.md b/docs/metadata/defs.md
new file mode 100755
index 0000000..9ef87db
--- /dev/null
+++ b/docs/metadata/defs.md
@@ -0,0 +1,68 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Public API of `@package_metadata`.
+
+<a id="PackageAttributeInfo"></a>
+
+## PackageAttributeInfo
+
+<pre>
+load("@package_metadata//:defs.bzl", "PackageAttributeInfo")
+
+PackageAttributeInfo(<a href="#PackageAttributeInfo-kind">kind</a>, <a href="#PackageAttributeInfo-attributes">attributes</a>, <a href="#PackageAttributeInfo-files">files</a>)
+</pre>
+
+Provider for declaring metadata about a Bazel package.
+
+**FIELDS**
+
+| Name  | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="PackageAttributeInfo-kind"></a>kind | The identifier of the attribute.<br><br>This should generally be in reverse DNS format (e.g., `com.example.foo`). | none |
+| <a id="PackageAttributeInfo-attributes"></a>attributes | The [File](https://bazel.build/rules/lib/builtins/File) containing the attributes.<br><br>The format of this file depends on the `kind` of attribute. Please consult the documentation of the attribute. | none |
+| <a id="PackageAttributeInfo-files"></a>files | A [depset](https://bazel.build/rules/lib/builtins/depset) of [File](https://bazel.build/rules/lib/builtins/File)s containing information about this attribute. | `[]` |
+
+
+<a id="PackageMetadataInfo"></a>
+
+## PackageMetadataInfo
+
+<pre>
+load("@package_metadata//:defs.bzl", "PackageMetadataInfo")
+
+PackageMetadataInfo(<a href="#PackageMetadataInfo-metadata">metadata</a>, <a href="#PackageMetadataInfo-files">files</a>)
+</pre>
+
+Provider for declaring metadata about a Bazel package.
+
+**FIELDS**
+
+| Name  | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="PackageMetadataInfo-metadata"></a>metadata | The [File](https://bazel.build/rules/lib/builtins/File) containing metadata about the package. | none |
+| <a id="PackageMetadataInfo-files"></a>files | A [depset](https://bazel.build/rules/lib/builtins/depset) of [File](https://bazel.build/rules/lib/builtins/File)s with metadata about the package, including transitive files from all attributes of the package. | `[]` |
+
+
+<a id="package_metadata"></a>
+
+## package_metadata
+
+<pre>
+load("@package_metadata//:defs.bzl", "package_metadata")
+
+package_metadata(*, <a href="#package_metadata-name">name</a>, <a href="#package_metadata-purl">purl</a>, <a href="#package_metadata-attributes">attributes</a>, <a href="#package_metadata-visibility">visibility</a>)
+</pre>
+
+
+
+**PARAMETERS**
+
+
+| Name  | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="package_metadata-name"></a>name |  <p align="center"> - </p>   |  none |
+| <a id="package_metadata-purl"></a>purl |  <p align="center"> - </p>   |  none |
+| <a id="package_metadata-attributes"></a>attributes |  <p align="center"> - </p>   |  `[]` |
+| <a id="package_metadata-visibility"></a>visibility |  <p align="center"> - </p>   |  `None` |
+
+
diff --git a/docs/metadata/providers/BUILD b/docs/metadata/providers/BUILD
new file mode 100644
index 0000000..c32c9c8
--- /dev/null
+++ b/docs/metadata/providers/BUILD
@@ -0,0 +1,32 @@
+load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
+load("@stardoc//stardoc:stardoc.bzl", "stardoc")
+
+stardoc(
+    name = "package_attribute_info",
+    out = "package_attribute_info.generated.md",
+    input = "@package_metadata//providers:package_attribute_info.bzl",
+    deps = [
+        "@package_metadata//providers:srcs",
+    ],
+)
+
+diff_test(
+    name = "package_attribute_info_test",
+    file1 = ":package_attribute_info",
+    file2 = "package_attribute_info.md",
+)
+
+stardoc(
+    name = "package_metadata_info",
+    out = "package_metadata_info.generated.md",
+    input = "@package_metadata//providers:package_metadata_info.bzl",
+    deps = [
+        "@package_metadata//providers:srcs",
+    ],
+)
+
+diff_test(
+    name = "package_metadata_info_test",
+    file1 = ":package_metadata_info",
+    file2 = "package_metadata_info.md",
+)
diff --git a/docs/metadata/providers/package_attribute_info.md b/docs/metadata/providers/package_attribute_info.md
new file mode 100755
index 0000000..38cc5c2
--- /dev/null
+++ b/docs/metadata/providers/package_attribute_info.md
@@ -0,0 +1,25 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Declares provider `PackageAttributeInfo`.
+
+<a id="PackageAttributeInfo"></a>
+
+## PackageAttributeInfo
+
+<pre>
+load("@package_metadata//providers:package_attribute_info.bzl", "PackageAttributeInfo")
+
+PackageAttributeInfo(<a href="#PackageAttributeInfo-kind">kind</a>, <a href="#PackageAttributeInfo-attributes">attributes</a>, <a href="#PackageAttributeInfo-files">files</a>)
+</pre>
+
+Provider for declaring metadata about a Bazel package.
+
+**FIELDS**
+
+| Name  | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="PackageAttributeInfo-kind"></a>kind | The identifier of the attribute.<br><br>This should generally be in reverse DNS format (e.g., `com.example.foo`). | none |
+| <a id="PackageAttributeInfo-attributes"></a>attributes | The [File](https://bazel.build/rules/lib/builtins/File) containing the attributes.<br><br>The format of this file depends on the `kind` of attribute. Please consult the documentation of the attribute. | none |
+| <a id="PackageAttributeInfo-files"></a>files | A [depset](https://bazel.build/rules/lib/builtins/depset) of [File](https://bazel.build/rules/lib/builtins/File)s containing information about this attribute. | `[]` |
+
+
diff --git a/docs/metadata/providers/package_metadata_info.md b/docs/metadata/providers/package_metadata_info.md
new file mode 100755
index 0000000..f609310
--- /dev/null
+++ b/docs/metadata/providers/package_metadata_info.md
@@ -0,0 +1,24 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Declares provider `PackageMetadataInfo`.
+
+<a id="PackageMetadataInfo"></a>
+
+## PackageMetadataInfo
+
+<pre>
+load("@package_metadata//providers:package_metadata_info.bzl", "PackageMetadataInfo")
+
+PackageMetadataInfo(<a href="#PackageMetadataInfo-metadata">metadata</a>, <a href="#PackageMetadataInfo-files">files</a>)
+</pre>
+
+Provider for declaring metadata about a Bazel package.
+
+**FIELDS**
+
+| Name  | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="PackageMetadataInfo-metadata"></a>metadata | The [File](https://bazel.build/rules/lib/builtins/File) containing metadata about the package. | none |
+| <a id="PackageMetadataInfo-files"></a>files | A [depset](https://bazel.build/rules/lib/builtins/depset) of [File](https://bazel.build/rules/lib/builtins/File)s with metadata about the package, including transitive files from all attributes of the package. | `[]` |
+
+
diff --git a/docs/metadata/rules/BUILD b/docs/metadata/rules/BUILD
new file mode 100644
index 0000000..06485a6
--- /dev/null
+++ b/docs/metadata/rules/BUILD
@@ -0,0 +1,17 @@
+load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
+load("@stardoc//stardoc:stardoc.bzl", "stardoc")
+
+stardoc(
+    name = "package_metadata",
+    out = "package_metadata.generated.md",
+    input = "@package_metadata//rules:package_metadata.bzl",
+    deps = [
+        "@package_metadata//rules:srcs",
+    ],
+)
+
+diff_test(
+    name = "package_metadata_test",
+    file1 = ":package_metadata",
+    file2 = "package_metadata.md",
+)
diff --git a/docs/metadata/rules/package_metadata.md b/docs/metadata/rules/package_metadata.md
new file mode 100755
index 0000000..367e1c3
--- /dev/null
+++ b/docs/metadata/rules/package_metadata.md
@@ -0,0 +1,27 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Declares rule `package_metadata`.
+
+<a id="package_metadata"></a>
+
+## package_metadata
+
+<pre>
+load("@package_metadata//rules:package_metadata.bzl", "package_metadata")
+
+package_metadata(*, <a href="#package_metadata-name">name</a>, <a href="#package_metadata-purl">purl</a>, <a href="#package_metadata-attributes">attributes</a>, <a href="#package_metadata-visibility">visibility</a>)
+</pre>
+
+
+
+**PARAMETERS**
+
+
+| Name  | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="package_metadata-name"></a>name |  <p align="center"> - </p>   |  none |
+| <a id="package_metadata-purl"></a>purl |  <p align="center"> - </p>   |  none |
+| <a id="package_metadata-attributes"></a>attributes |  <p align="center"> - </p>   |  `[]` |
+| <a id="package_metadata-visibility"></a>visibility |  <p align="center"> - </p>   |  `None` |
+
+
diff --git a/metadata/BUILD b/metadata/BUILD
index fe54861..2920f6d 100644
--- a/metadata/BUILD
+++ b/metadata/BUILD
@@ -1,4 +1,22 @@
-load("//:defs.bzl", "package_metadata")
+load("//rules:package_metadata.bzl", "package_metadata")
+
+exports_files(
+    [
+        "defs.bzl",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "srcs",
+    srcs = [
+        "defs.bzl",
+    ] + [
+        "//providers:srcs",
+        "//rules:srcs",
+    ],
+    visibility = ["//visibility:public"],
+)
 
 package_metadata(
     name = "package_metadata",
diff --git a/metadata/providers/BUILD b/metadata/providers/BUILD
index e69de29..d0c1a86 100644
--- a/metadata/providers/BUILD
+++ b/metadata/providers/BUILD
@@ -0,0 +1,16 @@
+exports_files(
+    [
+        "package_attribute_info.bzl",
+        "package_metadata_info.bzl",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "srcs",
+    srcs = [
+        "package_attribute_info.bzl",
+        "package_metadata_info.bzl",
+    ],
+    visibility = ["//visibility:public"],
+)
diff --git a/metadata/rules/BUILD b/metadata/rules/BUILD
index e69de29..fd08937 100644
--- a/metadata/rules/BUILD
+++ b/metadata/rules/BUILD
@@ -0,0 +1,16 @@
+exports_files(
+    [
+        "package_metadata.bzl",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "srcs",
+    srcs = [
+        "package_metadata.bzl",
+    ] + [
+        "//providers:srcs",
+    ],
+    visibility = ["//visibility:public"],
+)