{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://thinkube.io/schemas/thinkube-yaml-v1.0.json",
  "title": "ThinkubeDeployment",
  "description": "Schema for thinkube.yaml deployment descriptor",
  "type": "object",
  "required": [
    "apiVersion",
    "kind",
    "spec"
  ],
  "properties": {
    "apiVersion": {
      "type": "string",
      "enum": [
        "thinkube.io/v1"
      ]
    },
    "kind": {
      "type": "string",
      "enum": [
        "ThinkubeDeployment"
      ]
    },
    "metadata": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$|^\\{\\{\\s*project_name\\s*\\}\\}$"
        },
        "version": {
          "type": "string",
          "description": "The application's version, MAJOR.MINOR.PATCH. An application generated from a template starts at the template's value.",
          "pattern": "^\\d+\\.\\d+\\.\\d+$"
        }
      },
      "additionalProperties": false
    },
    "spec": {
      "$ref": "#/definitions/spec"
    }
  },
  "additionalProperties": false,
  "definitions": {
    "spec": {
      "type": "object",
      "required": [],
      "properties": {
        "deployment": {
          "$ref": "#/definitions/deployment"
        },
        "containers": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/definitions/container"
          }
        },
        "routes": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/route"
          }
        },
        "services": {
          "type": "array",
          "description": "Platform services the application needs. \"database\", \"cache\", \"storage\" and \"queue\" take an optional \":name\" suffix for a named instance; \"workflows\" does not, as an application has one.",
          "items": {
            "type": "string",
            "pattern": "^((database|cache|storage|queue)(:[a-z0-9-]+)?|workflows)$"
          }
        },
        "dependencies": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/dependency"
          }
        },
        "env": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/envVar"
          }
        },
        "parts": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/part"
          },
          "description": "Only when deployment.type is \"none\": the repository's trees, one toolchain each."
        },
        "deploy": {
          "$ref": "#/definitions/deploy"
        },
        "verify": {
          "$ref": "#/definitions/verify"
        },
        "docs": {
          "$ref": "#/definitions/docs"
        }
      },
      "additionalProperties": false,
      "anyOf": [
        {
          "required": [
            "containers"
          ]
        },
        {
          "properties": {
            "deployment": {
              "properties": {
                "type": {
                  "const": "none"
                }
              }
            }
          },
          "required": [
            "deployment"
          ]
        }
      ]
    },
    "deployment": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "app",
            "knative",
            "component",
            "none"
          ],
          "default": "app",
          "description": " \"none\": the platform does not deploy this repository; see spec.deploy."
        },
        "name": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]*$",
          "description": "Component only: fixed deployment name"
        },
        "replicas": {
          "type": "integer",
          "minimum": 0,
          "description": "Initial replica count. Set to 0 for gateway-managed components."
        },
        "gateway_managed": {
          "type": "boolean",
          "default": false,
          "description": "An external controller creates this component's pods at runtime, as the LLM gateway does per GPU. Its resting state is zero replicas, which is idle rather than disabled."
        },
        "minScale": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        },
        "maxScale": {
          "type": "integer",
          "minimum": 1,
          "default": 5
        },
        "containerConcurrency": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        },
        "timeoutSeconds": {
          "type": "integer",
          "minimum": 1,
          "maximum": 900,
          "default": 300
        }
      },
      "additionalProperties": false
    },
    "container": {
      "type": "object",
      "required": [
        "name",
        "build"
      ],
      "properties": {
        "name": {
          "type": "string",
          "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
        },
        "build": {
          "type": "string"
        },
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        },
        "size": {
          "type": "string",
          "enum": [
            "small",
            "medium",
            "large",
            "xlarge"
          ]
        },
        "health": {
          "type": "string",
          "pattern": "^/"
        },
        "schedule": {
          "type": "string"
        },
        "mounts": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[a-z0-9-]+:/[a-zA-Z0-9/_.-]+$"
          }
        },
        "gpu": {
          "$ref": "#/definitions/gpu"
        },
        "capabilities": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "large-uploads"
            ]
          }
        },
        "publicEnv": {
          "description": "Names of variables this container may show the browser. An allow-list; absent means nothing is published.",
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Z][A-Z0-9_]*$"
          }
        },
        "test": {
          "$ref": "#/definitions/test"
        },
        "migrations": {
          "$ref": "#/definitions/migrations"
        }
      },
      "additionalProperties": false
    },
    "gpu": {
      "type": "object",
      "required": [
        "count",
        "memory"
      ],
      "properties": {
        "count": {
          "type": "integer",
          "minimum": 1
        },
        "memory": {
          "type": "string",
          "pattern": "^[0-9]+Gi$"
        }
      },
      "additionalProperties": false
    },
    "test": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "default": false
        },
        "command": {
          "type": "string"
        },
        "image": {
          "type": "string"
        },
        "one": {
          "type": "string",
          "description": "How one test file runs; <file> is substituted with the path relative to the build directory."
        }
      },
      "additionalProperties": false,
      "if": {
        "properties": {
          "enabled": {
            "const": true
          }
        }
      },
      "then": {
        "required": [
          "command"
        ]
      }
    },
    "migrations": {
      "type": "object",
      "required": [
        "tool"
      ],
      "properties": {
        "tool": {
          "type": "string",
          "enum": [
            "alembic",
            "django",
            "flyway",
            "liquibase",
            "prisma",
            "sequelize",
            "gorm",
            "custom"
          ]
        },
        "auto": {
          "type": "boolean",
          "default": true
        }
      },
      "additionalProperties": false
    },
    "route": {
      "type": "object",
      "required": [
        "path",
        "to"
      ],
      "properties": {
        "path": {
          "type": "string",
          "pattern": "^/"
        },
        "to": {
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "dependency": {
      "type": "object",
      "required": [
        "name",
        "type",
        "env"
      ],
      "properties": {
        "name": {
          "type": "string"
        },
        "type": {
          "type": "string"
        },
        "env": {
          "type": "string",
          "pattern": "^[A-Z][A-Z0-9_]*$"
        }
      },
      "additionalProperties": false
    },
    "envVar": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "type": "string",
          "pattern": "^[A-Z][A-Z0-9_]*$"
        },
        "description": {
          "type": "string"
        },
        "default": {
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "part": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "root"
      ],
      "properties": {
        "name": {
          "type": "string"
        },
        "root": {
          "type": "string",
          "description": "Path relative to the repository root."
        },
        "test": {
          "$ref": "#/definitions/test"
        }
      }
    },
    "deploy": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "run": {
          "description": "The command that makes the repository live, or several to run in order. Absent when the merge itself made it live and only \"at\" is worth saying.",
          "oneOf": [
            {
              "type": "string"
            },
            {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          ]
        },
        "in": {
          "type": "string",
          "description": "The directory to run it in, when the command lives in another repository."
        },
        "at": {
          "type": "string",
          "format": "uri",
          "description": "Where the result can be seen."
        }
      }
    },
    "verify": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "still": {
          "description": "Commands that change nothing: a lint, a syntax check, a dry run. One or several.",
          "oneOf": [
            {
              "type": "string"
            },
            {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          ]
        },
        "apply": {
          "type": "string",
          "description": "The command that does the work."
        },
        "ask": {
          "type": "string",
          "description": "The command that reports whether anything is left to do."
        },
        "settled": {
          "type": "string",
          "description": "What that report must say, e.g. changed=0."
        }
      }
    },
    "docs": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "root": {
          "type": "string",
          "default": "docs",
          "description": "Where documentation lands. An antora.yml inside marks an Antora site."
        }
      }
    }
  },
  "allOf": [
    {
      "if": {
        "required": [
          "spec"
        ],
        "properties": {
          "spec": {
            "required": [
              "deployment"
            ],
            "properties": {
              "deployment": {
                "properties": {
                  "type": {
                    "const": "knative"
                  }
                },
                "required": [
                  "type"
                ]
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "spec": {
            "properties": {
              "containers": {
                "maxItems": 1,
                "items": {
                  "not": {
                    "anyOf": [
                      {
                        "required": [
                          "gpu"
                        ]
                      },
                      {
                        "required": [
                          "mounts"
                        ]
                      },
                      {
                        "required": [
                          "schedule"
                        ]
                      },
                      {
                        "required": [
                          "migrations"
                        ]
                      },
                      {
                        "required": [
                          "capabilities"
                        ]
                      }
                    ]
                  }
                }
              },
              "services": {
                "description": "Knative services keep a narrower set for cloud portability: \"storage\" and \"workflows\" are refused here.",
                "items": {
                  "type": "string",
                  "pattern": "^(database|cache|queue)(:[a-z0-9-]+)?$"
                }
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "required": [
          "spec"
        ],
        "properties": {
          "spec": {
            "required": [
              "deployment"
            ],
            "properties": {
              "deployment": {
                "properties": {
                  "type": {
                    "const": "component"
                  }
                },
                "required": [
                  "type"
                ]
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "spec": {
            "properties": {
              "deployment": {
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "required": [
          "spec"
        ],
        "properties": {
          "spec": {
            "required": [
              "deployment"
            ],
            "properties": {
              "deployment": {
                "properties": {
                  "type": {
                    "const": "knative"
                  }
                },
                "required": [
                  "type"
                ]
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "spec": {
            "properties": {
              "deployment": {
                "not": {
                  "required": [
                    "replicas"
                  ]
                }
              }
            }
          }
        }
      }
    }
  ]
}
