{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://choria.io/schemas/appbuilder/v1/application.json", "description": "Choria Application Builder Application Definition", "type": "object", "definitions": { "shortname": { "type": "string", "minLength": 1, "pattern": "^[a-z0-9_-]*$" }, "semver": { "type": "string", "description": "Semantic Versioning 2.0.0 version string", "minLength": 5, "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "description": { "type": "string", "description": "A human friendly description of what an item does", "minLength": 1 }, "commands": { "type": "array", "items": { "anyOf": [ {"$ref": "#/definitions/parent_command"}, {"$ref": "#/definitions/exec_command"}, {"$ref": "#/definitions/scaffold_command"}, {"$ref": "#/definitions/kv_command"}, {"$ref": "#/definitions/rpc_command"}, {"$ref": "#/definitions/discover_command"}, {"$ref": "#/definitions/form_command"} ] } }, "form_property": { "type": "object", "description": "Form input property", "required": [ "name", "description", "type" ], "properties": { "name": { "description": "A unique name for this form", "$ref": "#/definitions/shortname" }, "description": { "type": "string", "description": "A description for this property, shown before asking the value" }, "help": { "type": "string", "description": "Help shown to the user when '?' is pressed" }, "empty": { "type": "string", "description": "What data item to place when the user chooses to skip entering any values for a array or object", "enum": ["array", "object", "absent"] }, "type": { "type": "string", "description": "The type of data to request", "enum": ["string", "password", "object", "array", "integer", "float"] }, "conditional": { "type": "string", "description": "Conditional that would when evaluating to false prevent this property from being asked" }, "validation": { "type": "string", "description": "Validation for the input provided by the user" }, "required": { "type": "boolean", "default": false, "description": "Indicates if an answer is required" }, "default": { "description": "The default value to assign to this entry when not given" }, "enum": { "type": "string", "description": "A set of values used to limit what are valid inputs, presented as a select menu" }, "properties": { "type": "array", "description": "Additional properties for nested data types", "item": { "$ref": "#/definitions/form_property" } } } }, "generic_command_cheat": { "type": "object", "required": ["cheat"], "properties": { "label": { "description": "A unique label to give to this cheat, when not given will default to the name of the command it's associated with", "$ref": "#/definitions/shortname" }, "cheat": { "type": "string", "description": "The textual body of the cheat" } } }, "generic_flag": { "type": "object", "required": [ "name", "description" ], "properties": { "name": { "description": "A unique name for this argument", "$ref": "#/definitions/shortname" }, "description": { "$ref": "#/definitions/description" }, "required": { "type": "boolean", "description": "Indicates that this flag must be passed", "default": false }, "placeholder": { "type": "string", "description": "When generating help use this text instead of NAME to indicate the required value" }, "enum": { "type": "array", "description": "A list of valid values this flag will accept", "items": { "type": "string" } }, "default": { "oneOf": [ {"type": "string"}, {"type": "boolean"} ], "description": "A default value to assign to the argument" }, "bool": { "type": "boolean", "description": "Indicates this flag is a boolean flag that does not accept an argument", "default": false }, "validate": { "type": "string", "description": "A expr based validator for this specific argument" } } }, "scaffold_transform": { "type": "object", "description": "Transforms JSON data using the Scaffold system", "required": ["target"], "properties": { "target": { "type": "string", "description": "The directory to write the output to" }, "post": { "type": "array", "description": "List of post processing commands", "items": { "type": "object", "description": "Golang glob to match file against and a command to execute to process the file, support '{}' placeholders for filename" } }, "skip_empty": { "description": "Do not render empty files", "type": "boolean", "default": false }, "left_delimiter": { "type": "string", "description": "Custom delimiter to use at start of template logic" }, "right_delimiter": { "type": "string", "description": "Custom delimiter to use at end of template logic" } } }, "jq_transform": { "type": "object", "description": "Transforms JSON data using GOJQ", "required": ["query"], "properties": { "query": { "type": "string" } } }, "bar_graph_transform": { "type": "object", "description": "Draws a Bar Graph based on JSON input", "properties": { "width": { "type": "integer", "description": "The width of the bar component of the graph", "default": 40 }, "caption": { "type": "string", "description": "The caption to show along with the graph" }, "bytes": { "type": "boolean", "description": "When true renders the numbers as Bytes", "default": false } } }, "write_file_transform": { "type": "object", "required": ["file"], "properties": { "file": { "type": "string", "description": "Path to the file to write" }, "message": { "type": "string", "description": "The message to produce as output rather than the contents of the file" }, "replace": { "type": "boolean", "default": false, "description": "Replace existing files" } } }, "template_transform": { "type": "object", "description": "Produce text data using Go templates", "properties": { "contents": { "type": "string", "description": "The body of the template to parse" }, "source": { "type": "string", "description": "Loads the template from a file in this path" } } }, "report_transform": { "type": "object", "description": "Produce a textual report from row orientated data", "properties": { "name": { "type": "string", "description": "A name for the report, supports template variables" }, "body": { "type": "string", "description": "The report body definition" }, "header": { "type": "string", "description": "The report header definition" }, "footer": { "type": "string", "description": "The report footer definition" }, "rows_per_page": { "type": "integer", "description": "How many rows to render per page" }, "initial_query": { "type": "string", "description": "Allows row orientated data to be located using a GJSON query over object data" }, "source_file": { "type": "string", "description": "Load the report definition from a file" } } }, "line_graph_transform": { "type": "object", "description": "Draws a line graph based on JSON or text input", "properties": { "width": { "type": "integer", "description": "The width of the graph", "default": 40 }, "height": { "type": "integer", "description": "The height of the graph", "default": 20 }, "caption": { "type": "string", "description": "The caption to show along with the graph" }, "precision": { "type": "integer", "minimum": 0, "default": 0, "description": "The floating point precision to show in the graph legend" }, "json": { "type": "boolean", "description": "When true expect input in JSON format", "default": false } } }, "transform": { "type": "object", "properties": { "query": { "type": "string", "description": "Simplified version of the JQ transform" }, "jq": { "$ref": "#/definitions/jq_transform" }, "bar_graph": { "$ref": "#/definitions/bar_graph_transform" }, "line_graph": { "$ref": "#/definitions/line_graph_transform" }, "report": { "$ref": "#/definitions/report_transform" }, "template": { "$ref": "#/definitions/template_transform" }, "write_file": { "$ref": "#/definitions/write_file_transform" }, "pipeline": { "type": "array", "description": "List of transforms to apply in order", "items": { "anyOf": [ {"$ref": "#/definitions/jq_transform"}, {"$ref": "#/definitions/bar_graph_transform"}, {"$ref": "#/definitions/line_graph_transform"}, {"$ref": "#/definitions/report_transform"}, {"$ref": "#/definitions/template_transform"}, {"$ref": "#/definitions/write_file_transform"} ] } } } }, "generic_argument": { "type": "object", "required": [ "name", "description" ], "properties": { "name": { "description": "A unique name for this argument", "$ref": "#/definitions/shortname" }, "description": { "$ref": "#/definitions/description" }, "required": { "type": "boolean", "description": "Indicates that this flag must be passed", "default": false }, "enum": { "type": "array", "description": "A list of valid values this flag will accept", "items": { "type": "string" } }, "default": { "type": "string", "description": "A default value to assign to the argument" }, "validate": { "type": "string", "description": "A expr based validator for this specific argument" } } }, "generic_command": { "type": "object", "required": [ "name", "description" ], "properties": { "name": { "type": "string", "description": "A unique name for this command", "$ref": "#/definitions/shortname" }, "description": { "$ref": "#/definitions/description" }, "aliases": { "type": "array", "items": { "$ref": "#/definitions/shortname" } }, "banner": { "type": "string", "description": "When set shows this text before executing the command plugin, use to alert the user to some behavior, use confirm_prompt to force approval" }, "confirm_prompt": { "type": "string", "description": "When set always confirms the operation using the value as prompt" }, "cheat": { "$ref": "#/definitions/generic_command_cheat" } } }, "parent_command": { "description": "A command that does not do anything but serves as a anchor for sub commands", "type": "object", "allOf": [ {"$ref": "#/definitions/generic_command"}, { "type": "object", "properties": { "type": { "type": "string", "const": "parent" }, "commands": { "type": "array", "description": "Sub commands to add below this one", "$ref": "#/definitions/commands", "minItems": 1 } } } ] }, "rpc_filter": { "description": "Standard Choria RPC discovery options", "type": "object", "additionalItems": false, "properties": { "collective": { "type": "string", "description": "Target nodes in a specific sub-collective, defaults to configured main collective" }, "facts": { "type": "array", "description": "List of fact filters, like operatingsystem=CentOS, as typed in -F filters on the CLI", "items": { "type": "string" } }, "agents": { "type": "array", "description": "List of agents to require, as typed in -A filters on the CLI", "items": { "type": "string" } }, "classes": { "type": "array", "description": "List of classes to match on, as typed in -C filters on the CLI", "items": { "type": "string" } }, "identities": { "type": "array", "description": "List of identities to target, as typed in -I filters on the CLI", "items": { "type": "string" } }, "combined": { "type": "array", "description": "List of combined filters to match on targets, as typed in -W filters on the CLI", "items": { "type": "string" } }, "compound": { "type": "string", "description": "Compound filter to use, as typed in -S filters on the CLI" }, "discovery_method": { "type": "string", "description": "The discovery method to use", "enum": [ "mc", "broadcast", "choria", "puppetdb", "external", "flatfile", "file", "inventory" ] }, "discovery_timeout": { "type": "integer", "description": "Number of seconds to allow for discovery to complete", "minimum": 0 }, "dynamic_discovery_timeout": { "type": "boolean", "description": "Enables windowed dynamic discovery timeout" }, "nodes_file": { "type": "string", "description": "Path to a file listing node names in text or JSON format" }, "discovery_options": { "type": "object", "description": "Discovery options as a map of string values", "additionalItems": { "type": "string" } } } }, "discover_command": { "description": "A command that does Choria based node discovery. Only available within Choria Server.", "type": "object", "additionalItems": false, "allOf": [ { "$ref": "#/definitions/generic_command" }, { "type": "object", "required": [ "type" ], "properties": { "type": { "type": "string", "const": "discover" }, "filter": { "description": "Optional filters to apply to the request, will be merged with standard options if set", "$ref": "#/definitions/rpc_filter" }, "std_filters": { "type": "boolean", "description": "Enables standard RPC filters like -C, -I etc", "default": false }, "arguments": { "type": "array", "description": "List or arguments to accept after the command name", "items": { "$ref": "#/definitions/generic_argument" } }, "flags": { "type": "array", "description": "List of flags to add to the command", "items": { "allOf": [ { "$ref": "#/definitions/generic_flag" } ] } } } } ] }, "rpc_command": { "description": "A command that interact with the Choria RPC system. Available only within Choria Server not App Builder.", "additionalItems": false, "type": "object", "allOf": [ { "$ref": "#/definitions/generic_command" }, { "type": "object", "properties": { "type": { "type": "string", "const": "rpc" }, "commands": { "description": "Additional CLI commands to add", "$ref": "#/definitions/commands" }, "transform": { "$ref": "#/definitions/transform" }, "request": { "type": "object", "description": "Details of the RPC request", "properties": { "agent": { "type": "string", "description": "The agent to call", "$ref": "#/definitions/shortname" }, "action": { "type": "string", "description": "The action to call", "$ref": "#/definitions/shortname" }, "inputs": { "type": "object", "description": "Free form list of input arguments as a hash, values support template interpolation", "additionalProperties": { "type": "string" } }, "filter": { "description": "Optional filters to apply to the request, will be merged with standard options if set", "$ref": "#/definitions/rpc_filter" } } }, "std_filters": { "type": "boolean", "description": "Enables standard RPC filters like -C, -I etc", "default": false }, "output_format_flags": { "type": "boolean", "description": "Enable flags to adjust the output format like --json, --table etc", "default": false }, "output_format": { "type": "string", "description": "Sets a specific output format, not compatible with output_format_flags", "enum": [ "senders", "json", "table" ] }, "display_flag": { "type": "boolean", "description": "Enables the --display flag, not compatible with display", "default": false }, "display": { "type": "string", "description": "Force a specific display format to be used, not compatible with display_flags", "enum": [ "ok", "failed", "all", "none" ] }, "no_progress": { "type": "boolean", "description": "Disables the progress bar" }, "batch": { "type": "integer", "description": "Batch size to use when executing the RPC request, not compatible with batch_flags" }, "batch_sleep": { "type": "integer", "description": "When batch is given this is the seconds to sleep between batches, not compatible with batch_flags" }, "batch_flags": { "type": "boolean", "description": "Enables the --batch and --batch-sleep flags", "default": false }, "all_nodes_confirm_prompt": { "type": "string", "description": "Prompts for confirmation when no filter is active resulting on an action against all nodes" }, "flags": { "type": "array", "description": "List of flags to add to the command", "items": { "allOf": [ { "$ref": "#/definitions/generic_flag" }, { "type": "object", "properties": { "reply_filter": { "type": "string", "description": "Choria reply filter" } } } ] } } } } ] }, "kv_command": { "description": "A command that interact with the Choria Key-Value store. Available only within Choria Server not App Builder.", "type": "object", "additionalItems": false, "allOf": [ { "$ref": "#/definitions/generic_command" }, { "type": "object", "required": [ "type", "action", "bucket", "key" ], "properties": { "type": { "type": "string", "const": "kv" }, "commands": { "description": "Additional CLI commands to add", "$ref": "#/definitions/commands" }, "bucket": { "type": "string", "description": "The name of the Key-Value store bucket", "pattern": "\\A[a-zA-Z0-9_-]+\\z", "minLength": 1 }, "key": { "type": "string", "description": "The key to act on", "minLength": 1 }, "value": { "type": "string", "description": "The value to store for the put operation" }, "action": { "type": "string", "description": "The action to perform against the bucket and key", "enum": [ "get", "put", "del", "history" ] }, "json": { "type": "boolean", "description": "Renders the result in JSON format for get and history actions", "default": false }, "transform": { "type": "object", "description": "When the command returns JSON output, transform it using a GOJQ query", "allOf": [ {"$ref": "#/definitions/transform"} ] }, "flags": { "type": "array", "description": "Flags to accept", "items": { "$ref": "#/definitions/generic_flag" } }, "arguments": { "type": "array", "description": "Arguments to accept", "items": { "$ref": "#/definitions/generic_argument" } } } } ] }, "scaffold_command": { "description": "A command that scaffolds directories full of files", "type": "object", "allOf": [ {"$ref": "#/definitions/generic_command"}, { "type": "object", "properties": { "type": { "type": "string", "const": "scaffold" }, "target": { "type": "string", "description": "The directory where the files will be created in, may not exist" }, "source_directory": { "type": "string", "description": "The directory where the template files can be found, cannot be used with 'source'" }, "source": { "type": "object", "description": "Map holding file names and content. If the content is instead another object a directory is created and contained files are created within." }, "post": { "type": "array", "description": "List of post processing commands", "items": { "type": "object", "description": "Golang glob to match file against and a command to execute to process the file, support '{}' placeholders for filename" } }, "skip_empty": { "description": "Do not render empty files", "type": "boolean", "default": false }, "left_delimiter": { "type": "string", "description": "Custom delimiter to use at start of template logic" }, "right_delimiter": { "type": "string", "description": "Custom delimiter to use at end of template logic" }, "flags": { "type": "array", "description": "Flags to accept", "items": { "$ref": "#/definitions/generic_flag" } }, "arguments": { "type": "array", "description": "Arguments to accept", "items": { "$ref": "#/definitions/generic_argument" } } } } ] }, "form_command": { "description": "A guided wizard for generating complex data", "type": "object", "allOf": [ {"$ref": "#/definitions/generic_command"}, { "type": "object", "required": ["properties"], "properties": { "type": { "type": "string", "const": "form" }, "properties": { "description": "The list of properties to ask for", "type": "array", "items": { "$ref": "#/definitions/form_property" } } }, "transform": { "type": "object", "description": "When the command returns JSON output, transform it using a GOJQ query", "allOf": [ {"$ref": "#/definitions/transform"} ] }, "flags": { "type": "array", "description": "Flags to accept", "items": { "$ref": "#/definitions/generic_flag" } }, "arguments": { "type": "array", "description": "Arguments to accept", "items": { "$ref": "#/definitions/generic_argument" } } } ] }, "exec_command": { "description": "A command that executes other shell commands", "type": "object", "allOf": [ {"$ref": "#/definitions/generic_command"}, { "type": "object", "properties": { "type": { "type": "string", "const": "exec" }, "command": { "type": "string", "description": "The command to execute, supports templating" }, "script": { "type": "string", "description": "A script that will be run using a shell using -c" }, "shell": { "type": "string", "description": "Path to the shell to use when executing a script, else uses SHELL environment, bash or sh" }, "environment": { "type": "array", "description": "Additional environment values to set in VAR=VAL format", "items": { "type": "string" } }, "transform": { "type": "object", "description": "When the command returns JSON output, transform it using a GOJQ query", "allOf": [ {"$ref": "#/definitions/transform"} ] }, "flags": { "type": "array", "description": "Flags to accept", "items": { "$ref": "#/definitions/generic_flag" } }, "arguments": { "type": "array", "description": "Arguments to accept", "items": { "$ref": "#/definitions/generic_argument" } }, "commands": { "type": "array", "description": "Sub commands to add below this one", "$ref": "#/definitions/commands", "minItems": 1 }, "dir": { "type": "string", "description": "Runs the command or script in a specific directory" }, "no_helper": { "type": "boolean", "description": "Disables writing the shell helper temporary file" }, "backoff": { "type": "object", "required": ["max_attempts"], "description": "Configures retries of failing executions", "properties": { "max_attempts": { "type": "number", "description": "The maximum amount of times to retry the command", "minimum": 2 }, "max_sleep": { "type": "string", "description": "The maximum time to sleep between tries as a time format like 500ms, 1s or 1m", "default": "20s" }, "min_sleep": { "type": "string", "description": "The minimum time to sleep between tries as a time format like 1s or 1m", "default": "500ms" }, "steps": { "type": "number", "description": "How many steps to calculate between min and max, defaults to max_attempts", "minimum": 2 } } } } } ] } }, "properties": { "name": { "description": "A unique name for this application", "$ref": "#/definitions/shortname", "minLength": 1 }, "description": { "description": "A description of this application, shown in --help", "type": "string", "minLength": 1 }, "version": { "type": "string", "$ref": "#/definitions/semver", "description": "A SemVer version for this application", "minLength": 5 }, "author": { "type": "string", "description": "Who to contact about this application", "minLength": 1 }, "commands": { "type": "array", "description": "Sub commands to add below this one", "$ref": "#/definitions/commands", "minItems": 1 }, "help_template": { "type": "string", "enum": ["","default", "compact", "columns", "long"], "default": "default", "description": "Selects a help template to use for the app" }, "cheat": { "type": "object", "description": "Cheat sheet definitions, without a top level cheat entry cheats will not be enabled for a command", "properties": { "enabled": { "type": "boolean", "description": "Enables the cheat integration, only needed if a cheat body is not given at the top level" }, "tags": { "type": "array", "description": "List of tags to export to the cheat command when calling --save", "items": { "$ref": "#/definitions/shortname" } }, "label": { "description": "A unique label to give to this cheat, when not given will default to the name of the application", "$ref": "#/definitions/shortname" }, "cheat": { "type": "string", "description": "The textual body of the top level cheat" } } } } }