Validate constants before deploying

This makes our normal generators generate a *_unvalidated.json. The
constants_json rule will take that in and out a *.json file which is
validated and formatted.

Signed-off-by: Maxwell Henderson <mxwhenderson@gmail.com>
Change-Id: Iabd58bb072e0c3d300f8619d436b599d0f27b4b7
diff --git a/y2024/constants/BUILD b/y2024/constants/BUILD
index bcdd34a..1d18a63 100644
--- a/y2024/constants/BUILD
+++ b/y2024/constants/BUILD
@@ -1,5 +1,6 @@
 load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
 load("//tools/build_rules:template.bzl", "jinja2_template")
+load("//y2024/constants:validator.bzl", "constants_json")
 
 cc_library(
     name = "simulated_constants_sender",
@@ -17,7 +18,7 @@
 )
 
 jinja2_template(
-    name = "test_constants.json",
+    name = "test_constants_unvalidated.json",
     src = "test_constants.jinja2.json",
     includes = glob([
         "test_data/*.json",
@@ -39,7 +40,7 @@
 )
 
 jinja2_template(
-    name = "constants.json",
+    name = "constants_unvalidated.json",
     src = "constants.jinja2.json",
     includes = [
         "7971.json",
@@ -95,15 +96,27 @@
     ],
 )
 
-cc_test(
-    name = "constants_validator_test",
-    srcs = ["constants_validator_test.cc"],
-    data = [":constants.json"],
+cc_binary(
+    name = "constants_formatter",
+    srcs = ["constants_formatter.cc"],
+    data = [":constants_unvalidated.json"],
     visibility = ["//visibility:public"],
     deps = [
         ":constants_list_fbs",
+        "//aos:init",
         "//aos:json_to_flatbuffer",
-        "//aos/testing:googletest",
         "@com_github_google_glog//:glog",
     ],
 )
+
+constants_json(
+    name = "constants_json",
+    src = ":constants_unvalidated.json",
+    out = "constants.json",
+)
+
+constants_json(
+    name = "test_constants_json",
+    src = ":constants_unvalidated.json",
+    out = "test_constants.json",
+)
diff --git a/y2024/constants/constants_formatter.cc b/y2024/constants/constants_formatter.cc
new file mode 100644
index 0000000..d857407
--- /dev/null
+++ b/y2024/constants/constants_formatter.cc
@@ -0,0 +1,26 @@
+#include "glog/logging.h"
+
+#include "aos/flatbuffers.h"
+#include "aos/init.h"
+#include "aos/json_to_flatbuffer.h"
+#include "aos/util/file.h"
+#include "y2024/constants/constants_list_generated.h"
+
+int main(int argc, char **argv) {
+  ::aos::InitGoogle(&argc, &argv);
+
+  CHECK(argc == 3) << ": Expected input and output json files to be passed in.";
+
+  aos::FlatbufferDetachedBuffer<y2024::ConstantsList> constants =
+      aos::JsonFileToFlatbuffer<y2024::ConstantsList>(argv[1]);
+
+  // Make sure the file is valid json before we output a formatted version.
+  CHECK(constants.message().constants() != nullptr)
+      << ": Failed to parse " << std::string(argv[2]);
+
+  aos::util::WriteStringToFileOrDie(
+      std::string(argv[2]),
+      aos::FlatbufferToJson(constants, {.multi_line = true}));
+
+  return 0;
+}
diff --git a/y2024/constants/constants_validator_test.cc b/y2024/constants/constants_validator_test.cc
deleted file mode 100644
index 79dedf6..0000000
--- a/y2024/constants/constants_validator_test.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "glog/logging.h"
-#include "gtest/gtest.h"
-
-#include "aos/json_to_flatbuffer.h"
-#include "y2024/constants/constants_list_generated.h"
-
-namespace y2024::constants::testing {
-class ConstantsValidatorTest : public ::testing::Test {};
-
-TEST_F(ConstantsValidatorTest, CheckConstants) {
-  ASSERT_TRUE(aos::JsonFileToFlatbuffer<y2024::ConstantsList>(
-                  "y2024/constants/constants.json")
-                  .message()
-                  .constants() != nullptr)
-      << ": Failed to parse y2024/constants/constants.json";
-}
-
-}  // namespace y2024::constants::testing
diff --git a/y2024/constants/validator.bzl b/y2024/constants/validator.bzl
new file mode 100644
index 0000000..6121f58
--- /dev/null
+++ b/y2024/constants/validator.bzl
@@ -0,0 +1,13 @@
+load("@aspect_bazel_lib//lib:run_binary.bzl", "run_binary")
+
+# Validates the constants.json file and outputs a formatted version.
+# TODO(max): Make this generic/template it out into frc971
+def constants_json(name, src, out):
+    run_binary(
+        name = name,
+        tool = "//y2024/constants:constants_formatter",
+        srcs = [src],
+        outs = [out],
+        args = ["$(location %s)" % (src)] + ["$(location %s)" % (out)],
+        visibility = ["//visibility:public"],
+    )