Repository: dbt-labs/dbt-core

Issue:
Generate + host artifact JSON schemas
### Describe the feature
- Generate JSON schemas for `manifest.json`, `catalog.json`, `run_results.json`, `sources.json`
    - Using hologram to automate this as possible
    - Hand check for readability
- Host those schemas at schemas.getdbt.com/dbt/...
    - Artifact schemas as of dbt v0.18.1 should be v0 (they don't actually have version numbers, since no `metadata` field)
    - Artifact schemas as of dbt v0.19.0 should be v1
- Streamline as much of that process as possible

### Describe alternatives you've considered
- This is something we've publicly committed to doing before v0.19

### Who will this benefit?
- Users who parse dbt artifacts to power their deployments, wraparound tools, or approaches to metadata



Discussion:
- dbt repo will contain script to generate JSON schemas
- New repo to contain actual JSON schemas, add Netlify &rarr; schemas.getdbt.com
Cosmetic thing to keep track of: https://github.com/fishtown-analytics/schemas.getdbt.com/issues/1

Resolving patch:
diff --git a/core/dbt/contracts/util.py b/core/dbt/contracts/util.py
--- a/core/dbt/contracts/util.py
+++ b/core/dbt/contracts/util.py
@@ -126,7 +126,8 @@ def read(cls, path: str):
         return cls.from_dict(data)  # type: ignore
 
 
-BASE_SCHEMAS_URL = 'https://schemas.getdbt.com/dbt/{name}/v{version}.json'
+BASE_SCHEMAS_URL = 'https://schemas.getdbt.com/'
+SCHEMA_PATH = 'dbt/{name}/v{version}.json'
 
 
 @dataclasses.dataclass
@@ -134,12 +135,16 @@ class SchemaVersion:
     name: str
     version: int
 
-    def __str__(self) -> str:
-        return BASE_SCHEMAS_URL.format(
+    @property
+    def path(self) -> str:
+        return SCHEMA_PATH.format(
             name=self.name,
-            version=self.version,
+            version=self.version
         )
 
+    def __str__(self) -> str:
+        return BASE_SCHEMAS_URL + self.path
+
 
 SCHEMA_VERSION_KEY = 'dbt_schema_version'
 
diff --git a/scripts/collect-artifact-schema.py b/scripts/collect-artifact-schema.py
old mode 100644
new mode 100755
--- a/scripts/collect-artifact-schema.py
+++ b/scripts/collect-artifact-schema.py
@@ -1,31 +1,89 @@
 #!/usr/bin/env python
 from dataclasses import dataclass
-from typing import Dict, Any
+from argparse import ArgumentParser
+from pathlib import Path
 import json
+from typing import Type, Dict, Any
 
-from dbt.dataclass_schema import dbtClassMixin
 from dbt.contracts.graph.manifest import WritableManifest
 from dbt.contracts.results import (
     CatalogArtifact, RunResultsArtifact, FreshnessExecutionResultArtifact
 )
+from dbt.contracts.util import VersionedSchema
+from dbt.clients.system import write_file
 
 
 @dataclass
-class Schemas(dbtClassMixin):
-    manifest: Dict[str, Any]
-    catalog: Dict[str, Any]
-    run_results: Dict[str, Any]
-    freshness_results: Dict[str, Any]
+class ArtifactInfo:
+    path: str
+    name: str
+    json_schema: Dict[str, Any]
+
+    @classmethod
+    def from_artifact_cls(
+        cls,
+        artifact_cls: Type[VersionedSchema],
+    ) -> 'ArtifactInfo':
+        return cls(
+            path=artifact_cls.dbt_schema_version.path,
+            name=artifact_cls.dbt_schema_version.name,
+            json_schema=artifact_cls.json_schema()
+        )
+
+    def write_schema(self, dest_dir: Path):
+        write_file(
+            str(dest_dir / self.path),
+            json.dumps(self.json_schema, indent=2)
+        )
+
+
+@dataclass
+class Arguments:
+    path: Path
+
+    @classmethod
+    def parse(cls) -> 'Arguments':
+        parser = ArgumentParser(
+            prog="Collect and write dbt arfifact schema"
+        )
+        parser.add_argument(
+            '--path',
+            type=Path,
+            help='The dir to write artifact schema',
+        )
+
+        parsed = parser.parse_args()
+        return cls(
+            path=parsed.path
+        )
+
+
+def collect_artifact_schema(args: Arguments):
+    artifacts = [
+        FreshnessExecutionResultArtifact,
+        RunResultsArtifact,
+        CatalogArtifact,
+        WritableManifest
+    ]
+    artifact_infos = []
+    for artifact_cls in artifacts:
+        artifact_infos.append(ArtifactInfo.from_artifact_cls(artifact_cls))
+
+    if args and args.path is not None:
+        for artifact_info in artifact_infos:
+            dest_dir = args.path.resolve()
+            artifact_info.write_schema(dest_dir)
+    else:
+        artifacts_dict = {
+            artifact_info.name: artifact_info.json_schema
+            for artifact_info in artifact_infos
+        }
+        print(json.dumps(artifacts_dict))
 
 
 def main():
-    schemas = Schemas(
-        manifest=WritableManifest.json_schema(),
-        catalog=CatalogArtifact.json_schema(),
-        run_results=RunResultsArtifact.json_schema(),
-        freshness_results=FreshnessExecutionResultArtifact.json_schema(),
-    )
-    print(json.dumps(schemas.to_dict()))
+    parsed = Arguments.parse()
+    collect_artifact_schema(parsed)
 
 
 if __name__ == '__main__':


Test patch: