diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 3271f9ee1..b22a1d40b 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -86,15 +86,15 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run Lints run: | - cargo clippy --fix + cargo clippy cargo run -p rules_check - - name: Check for git diff after lint fix - run: | - if [[ $(git status --porcelain) ]]; then - git status - git diff - exit 1 - fi + # - name: Check for git diff after lint fix + # run: | + # if [[ $(git status --porcelain) ]]; then + # git status + # git diff + # exit 1 + # fi # check-dependencies: # name: Check Dependencies diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e010f357b..a4bc1b90c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,7 +63,7 @@ jobs: - name: 🛠️ Run Build run: cargo build -p pglt_cli --release --target ${{ matrix.config.target }} - # windows is a special snowflake to, it saves binaries as .exe + # windows is a special snowflake too, it saves binaries as .exe - name: 👦 Name the Binary if: matrix.config.os == 'windows-latest' run: | @@ -124,7 +124,9 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} body: ${{ steps.create_changelog.outputs.content }} tag_name: ${{ steps.create_changelog.outputs.version }} - files: pglt_* + files: | + pglt_* + docs/schemas/latest/schema.json fail_on_unmatched_files: true draft: true diff --git a/Cargo.lock b/Cargo.lock index 10a46ca40..68ee4c3f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -363,6 +363,7 @@ dependencies = [ "biome_rowan", "bitflags 2.6.0", "indexmap 2.7.0", + "schemars", "serde", ] @@ -994,6 +995,9 @@ dependencies = [ "pglt_workspace", "pulldown-cmark", "regex", + "schemars", + "serde", + "serde_json", "toml", ] @@ -2353,6 +2357,7 @@ dependencies = [ "biome_deserialize", "biome_deserialize_macros", "bpaf", + "indexmap 2.7.0", "pglt_analyse", "pglt_analyser", "pglt_console", diff --git a/crates/pglt_configuration/Cargo.toml b/crates/pglt_configuration/Cargo.toml index f9f4ce186..4c45bfbbf 100644 --- a/crates/pglt_configuration/Cargo.toml +++ b/crates/pglt_configuration/Cargo.toml @@ -12,9 +12,10 @@ version = "0.0.0" [dependencies] -biome_deserialize = { workspace = true } +biome_deserialize = { workspace = true, features = ["schema"] } biome_deserialize_macros = { workspace = true } bpaf = { workspace = true } +indexmap = { workspace = true } pglt_analyse = { workspace = true } pglt_analyser = { workspace = true } pglt_console = { workspace = true } @@ -30,4 +31,4 @@ toml = { workspace = true } doctest = false [features] -schema = ["dep:schemars"] +schema = ["dep:schemars", "schemars/indexmap"] diff --git a/crates/pglt_configuration/src/database.rs b/crates/pglt_configuration/src/database.rs index 2feb03304..2cf6cbd48 100644 --- a/crates/pglt_configuration/src/database.rs +++ b/crates/pglt_configuration/src/database.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; /// The configuration of the database connection. #[derive(Clone, Debug, Deserialize, Eq, Partial, PartialEq, Serialize)] #[partial(derive(Bpaf, Clone, Eq, PartialEq, Merge))] +#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))] #[partial(serde(rename_all = "snake_case", default, deny_unknown_fields))] pub struct DatabaseConfiguration { /// The host of the database. diff --git a/crates/pglt_configuration/src/files.rs b/crates/pglt_configuration/src/files.rs index 039d785be..bf2a5c4f8 100644 --- a/crates/pglt_configuration/src/files.rs +++ b/crates/pglt_configuration/src/files.rs @@ -13,6 +13,7 @@ pub const DEFAULT_FILE_SIZE_LIMIT: NonZeroU64 = /// The configuration of the filesystem #[derive(Clone, Debug, Deserialize, Eq, Partial, PartialEq, Serialize)] #[partial(derive(Bpaf, Clone, Eq, PartialEq, Merge))] +#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))] #[partial(serde(rename_all = "snake_case", default, deny_unknown_fields))] pub struct FilesConfiguration { /// The maximum allowed size for source code files in bytes. Files above diff --git a/crates/pglt_configuration/src/migrations.rs b/crates/pglt_configuration/src/migrations.rs index 2c7842203..2fa99c695 100644 --- a/crates/pglt_configuration/src/migrations.rs +++ b/crates/pglt_configuration/src/migrations.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Eq, Partial, PartialEq, Serialize, Default)] #[partial(derive(Bpaf, Clone, Eq, PartialEq, Merge))] #[partial(serde(rename_all = "snake_case", default, deny_unknown_fields))] +#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))] pub struct MigrationsConfiguration { /// The directory where the migration files are stored #[partial(bpaf(long("migrations-dir")))] diff --git a/docs/codegen/Cargo.toml b/docs/codegen/Cargo.toml index c7898c7a2..646490f82 100644 --- a/docs/codegen/Cargo.toml +++ b/docs/codegen/Cargo.toml @@ -16,8 +16,12 @@ regex = { workspace = true } toml = { workspace = true } anyhow = { workspace = true } bpaf = { workspace = true, features = ["docgen"] } +schemars = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +pulldown-cmark = "0.12.2" -pglt_configuration = { workspace = true } +pglt_configuration = { workspace = true, features = ["schema"] } pglt_flags = { workspace = true } pglt_cli = { workspace = true } pglt_analyse = { workspace = true } @@ -28,5 +32,4 @@ pglt_workspace = { workspace = true } pglt_statement_splitter = { workspace = true } pglt_console = { workspace = true } biome_string_case = { workspace = true } -pulldown-cmark = "0.12.2" diff --git a/docs/codegen/src/lib.rs b/docs/codegen/src/lib.rs index 6ff084603..df49d5424 100644 --- a/docs/codegen/src/lib.rs +++ b/docs/codegen/src/lib.rs @@ -4,5 +4,6 @@ pub mod env_variables; pub mod rules_docs; pub mod rules_index; pub mod rules_sources; +pub mod schema; mod utils; diff --git a/docs/codegen/src/main.rs b/docs/codegen/src/main.rs index a03a1bf05..dfecbd533 100644 --- a/docs/codegen/src/main.rs +++ b/docs/codegen/src/main.rs @@ -7,6 +7,7 @@ use docs_codegen::env_variables::generate_env_variables; use docs_codegen::rules_docs::generate_rules_docs; use docs_codegen::rules_index::generate_rules_index; use docs_codegen::rules_sources::generate_rule_sources; +use docs_codegen::schema::generate_schema; fn docs_root() -> PathBuf { let dir = @@ -23,6 +24,7 @@ fn main() -> anyhow::Result<()> { generate_rules_docs(&docs_root)?; generate_rules_index(&docs_root)?; generate_rule_sources(&docs_root)?; + generate_schema(&docs_root)?; Ok(()) } diff --git a/docs/codegen/src/schema.rs b/docs/codegen/src/schema.rs new file mode 100644 index 000000000..f1b51ff6a --- /dev/null +++ b/docs/codegen/src/schema.rs @@ -0,0 +1,201 @@ +use pglt_configuration::{PartialConfiguration, VERSION}; +use schemars::{ + schema::{RootSchema, Schema, SchemaObject}, + schema_for, +}; +use serde_json::to_string_pretty; +use std::{fs, path::Path}; + +/// Generates the lint rules index. +/// +/// * `docs_dir`: Path to the docs directory. +pub fn generate_schema(docs_dir: &Path) -> anyhow::Result<()> { + let schemas_dir = docs_dir.join("schemas"); + let latest_schema_dir = schemas_dir.join("latest"); + let latest_schema_path = latest_schema_dir.join("schema.json"); + + let version_schema_dir = schemas_dir.join(VERSION); + let version_schema_path = version_schema_dir.join("schema.json"); + + if !latest_schema_dir.exists() { + fs::create_dir_all(&latest_schema_dir)?; + } + + if !version_schema_dir.exists() { + fs::create_dir_all(&version_schema_dir)?; + } + + let schema_content = get_configuration_schema_content()?; + + fs::write(latest_schema_path, &schema_content)?; + fs::write(version_schema_path, &schema_content)?; + + Ok(()) +} + +/// Get the content of the configuration schema +pub(crate) fn get_configuration_schema_content() -> anyhow::Result { + let schema = rename_partial_references_in_schema(schema_for!(PartialConfiguration)); + + Ok(to_string_pretty(&schema)?) +} + +/// Strips all "Partial" prefixes from type names in the schema. +/// +/// We do this to avoid leaking our `Partial` derive macro to the outside world, +/// since it should be just an implementation detail. +fn rename_partial_references_in_schema(mut schema: RootSchema) -> RootSchema { + if let Some(meta) = schema.schema.metadata.as_mut() { + if let Some(title) = meta.title.as_ref() { + if let Some(stripped) = title.strip_prefix("Partial") { + meta.title = Some(stripped.to_string()); + } else if title == "RuleWithOptions_for_Null" { + meta.title = Some("RuleWithNoOptions".to_string()); + } else if title == "RuleWithFixOptions_for_Null" { + meta.title = Some("RuleWithFixNoOptions".to_string()); + } else if title == "RuleConfiguration_for_Null" { + meta.title = Some("RuleConfiguration".to_string()); + } else if title == "RuleFixConfiguration_for_Null" { + meta.title = Some("RuleFixConfiguration".to_string()); + } else if let Some(stripped) = title.strip_prefix("RuleWithOptions_for_") { + meta.title = Some(format!("RuleWith{stripped}")); + } else if let Some(stripped) = title.strip_prefix("RuleWithFixOptions_for_") { + meta.title = Some(format!("RuleWith{stripped}")); + } else if let Some(stripped) = title + .strip_prefix("RuleConfiguration_for_") + .map(|x| x.strip_suffix("Options").unwrap_or(x)) + { + meta.title = Some(format!("{stripped}Configuration")); + } else if let Some(stripped) = title + .strip_prefix("RuleFixConfiguration_for_") + .map(|x| x.strip_suffix("Options").unwrap_or(x)) + { + meta.title = Some(format!("{stripped}Configuration")); + } + } + } + + rename_partial_references_in_schema_object(&mut schema.schema); + + schema.definitions = schema + .definitions + .into_iter() + .map(|(mut key, mut schema)| { + if let Some(stripped) = key.strip_prefix("Partial") { + key = stripped.to_string(); + } else if key == "RuleWithOptions_for_Null" || key == "RuleWithFixOptions_for_Null" { + key = if key == "RuleWithOptions_for_Null" { + "RuleWithNoOptions".to_string() + } else { + "RuleWithFixNoOptions".to_string() + }; + if let Schema::Object(schema_object) = &mut schema { + if let Some(object) = &mut schema_object.object { + object.required.remove("options"); + object.properties.remove("options"); + } + } + } else if key == "RuleConfiguration_for_Null" { + key = "RuleConfiguration".to_string(); + } else if key == "RuleFixConfiguration_for_Null" { + key = "RuleFixConfiguration".to_string(); + } else if let Some(stripped) = key.strip_prefix("RuleWithOptions_for_") { + key = format!("RuleWith{stripped}"); + } else if let Some(stripped) = key.strip_prefix("RuleWithFixOptions_for_") { + key = format!("RuleWith{stripped}"); + } else if let Some(stripped) = key + .strip_prefix("RuleConfiguration_for_") + .map(|x| x.strip_suffix("Options").unwrap_or(x)) + { + key = format!("{stripped}Configuration"); + } else if let Some(stripped) = key + .strip_prefix("RuleFixConfiguration_for_") + .map(|x| x.strip_suffix("Options").unwrap_or(x)) + { + key = format!("{stripped}Configuration"); + } + + if let Schema::Object(object) = &mut schema { + rename_partial_references_in_schema_object(object); + } + + (key, schema) + }) + .collect(); + + schema +} + +fn rename_partial_references_in_schema_object(object: &mut SchemaObject) { + if let Some(object) = &mut object.object { + for prop_schema in object.properties.values_mut() { + if let Schema::Object(object) = prop_schema { + rename_partial_references_in_schema_object(object); + } + } + } + + if let Some(reference) = &mut object.reference { + if let Some(stripped) = reference.strip_prefix("#/definitions/Partial") { + *reference = format!("#/definitions/{stripped}"); + } else if reference == "#/definitions/RuleWithOptions_for_Null" { + *reference = "#/definitions/RuleWithNoOptions".to_string(); + } else if reference == "#/definitions/RuleWithFixOptions_for_Null" { + *reference = "#/definitions/RuleWithFixNoOptions".to_string(); + } else if reference == "#/definitions/RuleConfiguration_for_Null" { + *reference = "#/definitions/RuleConfiguration".to_string(); + } else if reference == "#/definitions/RuleFixConfiguration_for_Null" { + *reference = "#/definitions/RuleFixConfiguration".to_string(); + } else if let Some(stripped) = reference.strip_prefix("#/definitions/RuleWithOptions_for_") + { + *reference = format!("#/definitions/RuleWith{stripped}"); + } else if let Some(stripped) = + reference.strip_prefix("#/definitions/RuleWithFixOptions_for_") + { + *reference = format!("#/definitions/RuleWith{stripped}"); + } else if let Some(stripped) = reference + .strip_prefix("#/definitions/RuleConfiguration_for_") + .map(|x| x.strip_suffix("Options").unwrap_or(x)) + { + *reference = format!("#/definitions/{stripped}Configuration"); + } else if let Some(stripped) = reference + .strip_prefix("#/definitions/RuleFixConfiguration_for_") + .map(|x| x.strip_suffix("Options").unwrap_or(x)) + { + *reference = format!("#/definitions/{stripped}Configuration"); + } + } + + if let Some(subschemas) = &mut object.subschemas { + rename_partial_references_in_optional_schema_vec(&mut subschemas.all_of); + rename_partial_references_in_optional_schema_vec(&mut subschemas.any_of); + rename_partial_references_in_optional_schema_vec(&mut subschemas.one_of); + + rename_partial_references_in_optional_schema_box(&mut subschemas.not); + rename_partial_references_in_optional_schema_box(&mut subschemas.if_schema); + rename_partial_references_in_optional_schema_box(&mut subschemas.then_schema); + rename_partial_references_in_optional_schema_box(&mut subschemas.else_schema); + } +} + +fn rename_partial_references_in_optional_schema_box(schema: &mut Option>) { + if let Some(schema) = schema { + if let Schema::Object(object) = schema.as_mut() { + rename_partial_references_in_schema_object(object); + } + } +} + +fn rename_partial_references_in_optional_schema_vec(schemas: &mut Option>) { + if let Some(schemas) = schemas { + rename_partial_references_in_schema_slice(schemas); + } +} + +fn rename_partial_references_in_schema_slice(schemas: &mut [Schema]) { + for schema in schemas { + if let Schema::Object(object) = schema { + rename_partial_references_in_schema_object(object); + } + } +} diff --git a/docs/codegen/src/utils.rs b/docs/codegen/src/utils.rs index 7770a5085..5646468eb 100644 --- a/docs/codegen/src/utils.rs +++ b/docs/codegen/src/utils.rs @@ -19,9 +19,9 @@ pub(crate) fn replace_section( #[derive(Default)] pub(crate) struct LintRulesVisitor { /// This is mapped to: - /// group (e.g. "safety") -> - /// where is: - /// -> metadata + /// - group (correctness) -> list of rules + /// - list or rules is mapped to + /// - rule name -> metadata pub(crate) groups: BTreeMap<&'static str, BTreeMap<&'static str, RuleMetadata>>, } diff --git a/docs/schemas/0.0.0/schema.json b/docs/schemas/0.0.0/schema.json new file mode 100644 index 000000000..05b1f9968 --- /dev/null +++ b/docs/schemas/0.0.0/schema.json @@ -0,0 +1,399 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Configuration", + "description": "The configuration that is contained inside the configuration file.", + "type": "object", + "properties": { + "db": { + "description": "The configuration of the database connection", + "anyOf": [ + { + "$ref": "#/definitions/DatabaseConfiguration" + }, + { + "type": "null" + } + ] + }, + "files": { + "description": "The configuration of the filesystem", + "anyOf": [ + { + "$ref": "#/definitions/FilesConfiguration" + }, + { + "type": "null" + } + ] + }, + "linter": { + "description": "The configuration for the linter", + "anyOf": [ + { + "$ref": "#/definitions/LinterConfiguration" + }, + { + "type": "null" + } + ] + }, + "migrations": { + "description": "Configure migrations", + "anyOf": [ + { + "$ref": "#/definitions/MigrationsConfiguration" + }, + { + "type": "null" + } + ] + }, + "vcs": { + "description": "The configuration of the VCS integration", + "anyOf": [ + { + "$ref": "#/definitions/VcsConfiguration" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "DatabaseConfiguration": { + "description": "The configuration of the database connection.", + "type": "object", + "properties": { + "conn_timeout_secs": { + "description": "The connection timeout in seconds.", + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0.0 + }, + "database": { + "description": "The name of the database.", + "type": [ + "string", + "null" + ] + }, + "host": { + "description": "The host of the database.", + "type": [ + "string", + "null" + ] + }, + "password": { + "description": "The password to connect to the database.", + "type": [ + "string", + "null" + ] + }, + "port": { + "description": "The port of the database.", + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0.0 + }, + "username": { + "description": "The username to connect to the database.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "FilesConfiguration": { + "description": "The configuration of the filesystem", + "type": "object", + "properties": { + "ignore": { + "description": "A list of Unix shell style patterns. Will ignore files/folders that will match these patterns.", + "anyOf": [ + { + "$ref": "#/definitions/StringSet" + }, + { + "type": "null" + } + ] + }, + "include": { + "description": "A list of Unix shell style patterns. Will handle only those files/folders that will match these patterns.", + "anyOf": [ + { + "$ref": "#/definitions/StringSet" + }, + { + "type": "null" + } + ] + }, + "max_size": { + "description": "The maximum allowed size for source code files in bytes. Files above this limit will be ignored for performance reasons. Defaults to 1 MiB", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 1.0 + } + }, + "additionalProperties": false + }, + "LinterConfiguration": { + "type": "object", + "properties": { + "enabled": { + "description": "if `false`, it disables the feature and the linter won't be executed. `true` by default", + "type": [ + "boolean", + "null" + ] + }, + "ignore": { + "description": "A list of Unix shell style patterns. The formatter will ignore files/folders that will match these patterns.", + "anyOf": [ + { + "$ref": "#/definitions/StringSet" + }, + { + "type": "null" + } + ] + }, + "include": { + "description": "A list of Unix shell style patterns. The formatter will include files/folders that will match these patterns.", + "anyOf": [ + { + "$ref": "#/definitions/StringSet" + }, + { + "type": "null" + } + ] + }, + "rules": { + "description": "List of rules", + "anyOf": [ + { + "$ref": "#/definitions/Rules" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MigrationsConfiguration": { + "description": "The configuration of the filesystem", + "type": "object", + "properties": { + "after": { + "description": "Ignore any migrations before this timestamp", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "migrations_dir": { + "description": "The directory where the migration files are stored", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "RuleConfiguration": { + "anyOf": [ + { + "$ref": "#/definitions/RulePlainConfiguration" + }, + { + "$ref": "#/definitions/RuleWithNoOptions" + } + ] + }, + "RulePlainConfiguration": { + "type": "string", + "enum": [ + "warn", + "error", + "info", + "off" + ] + }, + "RuleWithNoOptions": { + "type": "object", + "required": [ + "level" + ], + "properties": { + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [ + { + "$ref": "#/definitions/RulePlainConfiguration" + } + ] + } + }, + "additionalProperties": false + }, + "Rules": { + "type": "object", + "properties": { + "all": { + "description": "It enables ALL rules. The rules that belong to `nursery` won't be enabled.", + "type": [ + "boolean", + "null" + ] + }, + "recommended": { + "description": "It enables the lint rules recommended by PgLT. `true` by default.", + "type": [ + "boolean", + "null" + ] + }, + "safety": { + "anyOf": [ + { + "$ref": "#/definitions/Safety" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Safety": { + "description": "A list of rules that belong to this group", + "type": "object", + "properties": { + "all": { + "description": "It enables ALL rules for this group.", + "type": [ + "boolean", + "null" + ] + }, + "banDropColumn": { + "description": "Dropping a column may break existing clients.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] + }, + "banDropNotNull": { + "description": "Dropping a NOT NULL constraint may break existing clients.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] + }, + "recommended": { + "description": "It enables the recommended rules for this group", + "type": [ + "boolean", + "null" + ] + } + }, + "additionalProperties": false + }, + "StringSet": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "VcsClientKind": { + "oneOf": [ + { + "description": "Integration with the git client as VCS", + "type": "string", + "enum": [ + "git" + ] + } + ] + }, + "VcsConfiguration": { + "description": "Set of properties to integrate with a VCS software.", + "type": "object", + "properties": { + "client_kind": { + "description": "The kind of client.", + "anyOf": [ + { + "$ref": "#/definitions/VcsClientKind" + }, + { + "type": "null" + } + ] + }, + "default_branch": { + "description": "The main branch of the project", + "type": [ + "string", + "null" + ] + }, + "enabled": { + "description": "Whether we should integrate itself with the VCS client", + "type": [ + "boolean", + "null" + ] + }, + "root": { + "description": "The folder where we should check for VCS files. By default, we will use the same folder where `pglt.toml` was found.\n\nIf we can't find the configuration, it will attempt to use the current working directory. If no current working directory can't be found, we won't use the VCS integration, and a diagnostic will be emitted", + "type": [ + "string", + "null" + ] + }, + "use_ignore_file": { + "description": "Whether we should use the VCS ignore file. When [true], we will ignore the files specified in the ignore file.", + "type": [ + "boolean", + "null" + ] + } + }, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/docs/schemas/latest/schema.json b/docs/schemas/latest/schema.json new file mode 100644 index 000000000..05b1f9968 --- /dev/null +++ b/docs/schemas/latest/schema.json @@ -0,0 +1,399 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Configuration", + "description": "The configuration that is contained inside the configuration file.", + "type": "object", + "properties": { + "db": { + "description": "The configuration of the database connection", + "anyOf": [ + { + "$ref": "#/definitions/DatabaseConfiguration" + }, + { + "type": "null" + } + ] + }, + "files": { + "description": "The configuration of the filesystem", + "anyOf": [ + { + "$ref": "#/definitions/FilesConfiguration" + }, + { + "type": "null" + } + ] + }, + "linter": { + "description": "The configuration for the linter", + "anyOf": [ + { + "$ref": "#/definitions/LinterConfiguration" + }, + { + "type": "null" + } + ] + }, + "migrations": { + "description": "Configure migrations", + "anyOf": [ + { + "$ref": "#/definitions/MigrationsConfiguration" + }, + { + "type": "null" + } + ] + }, + "vcs": { + "description": "The configuration of the VCS integration", + "anyOf": [ + { + "$ref": "#/definitions/VcsConfiguration" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "DatabaseConfiguration": { + "description": "The configuration of the database connection.", + "type": "object", + "properties": { + "conn_timeout_secs": { + "description": "The connection timeout in seconds.", + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0.0 + }, + "database": { + "description": "The name of the database.", + "type": [ + "string", + "null" + ] + }, + "host": { + "description": "The host of the database.", + "type": [ + "string", + "null" + ] + }, + "password": { + "description": "The password to connect to the database.", + "type": [ + "string", + "null" + ] + }, + "port": { + "description": "The port of the database.", + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0.0 + }, + "username": { + "description": "The username to connect to the database.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "FilesConfiguration": { + "description": "The configuration of the filesystem", + "type": "object", + "properties": { + "ignore": { + "description": "A list of Unix shell style patterns. Will ignore files/folders that will match these patterns.", + "anyOf": [ + { + "$ref": "#/definitions/StringSet" + }, + { + "type": "null" + } + ] + }, + "include": { + "description": "A list of Unix shell style patterns. Will handle only those files/folders that will match these patterns.", + "anyOf": [ + { + "$ref": "#/definitions/StringSet" + }, + { + "type": "null" + } + ] + }, + "max_size": { + "description": "The maximum allowed size for source code files in bytes. Files above this limit will be ignored for performance reasons. Defaults to 1 MiB", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 1.0 + } + }, + "additionalProperties": false + }, + "LinterConfiguration": { + "type": "object", + "properties": { + "enabled": { + "description": "if `false`, it disables the feature and the linter won't be executed. `true` by default", + "type": [ + "boolean", + "null" + ] + }, + "ignore": { + "description": "A list of Unix shell style patterns. The formatter will ignore files/folders that will match these patterns.", + "anyOf": [ + { + "$ref": "#/definitions/StringSet" + }, + { + "type": "null" + } + ] + }, + "include": { + "description": "A list of Unix shell style patterns. The formatter will include files/folders that will match these patterns.", + "anyOf": [ + { + "$ref": "#/definitions/StringSet" + }, + { + "type": "null" + } + ] + }, + "rules": { + "description": "List of rules", + "anyOf": [ + { + "$ref": "#/definitions/Rules" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MigrationsConfiguration": { + "description": "The configuration of the filesystem", + "type": "object", + "properties": { + "after": { + "description": "Ignore any migrations before this timestamp", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "migrations_dir": { + "description": "The directory where the migration files are stored", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "RuleConfiguration": { + "anyOf": [ + { + "$ref": "#/definitions/RulePlainConfiguration" + }, + { + "$ref": "#/definitions/RuleWithNoOptions" + } + ] + }, + "RulePlainConfiguration": { + "type": "string", + "enum": [ + "warn", + "error", + "info", + "off" + ] + }, + "RuleWithNoOptions": { + "type": "object", + "required": [ + "level" + ], + "properties": { + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [ + { + "$ref": "#/definitions/RulePlainConfiguration" + } + ] + } + }, + "additionalProperties": false + }, + "Rules": { + "type": "object", + "properties": { + "all": { + "description": "It enables ALL rules. The rules that belong to `nursery` won't be enabled.", + "type": [ + "boolean", + "null" + ] + }, + "recommended": { + "description": "It enables the lint rules recommended by PgLT. `true` by default.", + "type": [ + "boolean", + "null" + ] + }, + "safety": { + "anyOf": [ + { + "$ref": "#/definitions/Safety" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Safety": { + "description": "A list of rules that belong to this group", + "type": "object", + "properties": { + "all": { + "description": "It enables ALL rules for this group.", + "type": [ + "boolean", + "null" + ] + }, + "banDropColumn": { + "description": "Dropping a column may break existing clients.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] + }, + "banDropNotNull": { + "description": "Dropping a NOT NULL constraint may break existing clients.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] + }, + "recommended": { + "description": "It enables the recommended rules for this group", + "type": [ + "boolean", + "null" + ] + } + }, + "additionalProperties": false + }, + "StringSet": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "VcsClientKind": { + "oneOf": [ + { + "description": "Integration with the git client as VCS", + "type": "string", + "enum": [ + "git" + ] + } + ] + }, + "VcsConfiguration": { + "description": "Set of properties to integrate with a VCS software.", + "type": "object", + "properties": { + "client_kind": { + "description": "The kind of client.", + "anyOf": [ + { + "$ref": "#/definitions/VcsClientKind" + }, + { + "type": "null" + } + ] + }, + "default_branch": { + "description": "The main branch of the project", + "type": [ + "string", + "null" + ] + }, + "enabled": { + "description": "Whether we should integrate itself with the VCS client", + "type": [ + "boolean", + "null" + ] + }, + "root": { + "description": "The folder where we should check for VCS files. By default, we will use the same folder where `pglt.toml` was found.\n\nIf we can't find the configuration, it will attempt to use the current working directory. If no current working directory can't be found, we won't use the VCS integration, and a diagnostic will be emitted", + "type": [ + "string", + "null" + ] + }, + "use_ignore_file": { + "description": "Whether we should use the VCS ignore file. When [true], we will ignore the files specified in the ignore file.", + "type": [ + "boolean", + "null" + ] + } + }, + "additionalProperties": false + } + } +} \ No newline at end of file