{"id":49853246,"url":"https://github.com/yakdriver/swissshepherd","last_synced_at":"2026-05-18T20:01:23.759Z","repository":{"id":357221913,"uuid":"1235843848","full_name":"YakDriver/swissshepherd","owner":"YakDriver","description":"Documentation checker for Terraform providers","archived":false,"fork":false,"pushed_at":"2026-05-14T17:08:52.000Z","size":179,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-14T18:47:06.304Z","etag":null,"topics":["linter","terraform","terraform-provider"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/YakDriver.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T17:55:49.000Z","updated_at":"2026-05-14T16:51:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/YakDriver/swissshepherd","commit_stats":null,"previous_names":["yakdriver/swissshepherd"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/YakDriver/swissshepherd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YakDriver%2Fswissshepherd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YakDriver%2Fswissshepherd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YakDriver%2Fswissshepherd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YakDriver%2Fswissshepherd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/YakDriver","download_url":"https://codeload.github.com/YakDriver/swissshepherd/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YakDriver%2Fswissshepherd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33189279,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-18T09:27:30.708Z","status":"ssl_error","status_checked_at":"2026-05-18T09:27:28.300Z","response_time":71,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["linter","terraform","terraform-provider"],"created_at":"2026-05-14T18:00:57.275Z","updated_at":"2026-05-18T20:01:23.750Z","avatar_url":"https://github.com/YakDriver.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# swissshepherd\n\u003c!-- Copyright IBM Corp. 2019, 2026 --\u003e\n\u003c!-- SPDX-License-Identifier: MPL-2.0 --\u003e\n\nA documentation checker for Terraform providers. Validates that provider documentation accurately reflects the provider schema.\n\n## Table of contents\n\n- [What it checks](#what-it-checks)\n- [Installation](#installation)\n- [Quick start](#quick-start)\n- [CLI reference](#cli-reference)\n- [Configuration](#configuration)\n- [Rules reference](#rules-reference)\n- [Path scoping](#path-scoping)\n- [Type system](#type-system)\n- [Heading templates](#heading-templates)\n- [Schema generation](#schema-generation)\n- [Design](#design)\n\n## What it checks\n\nswissshepherd compares a Terraform provider's schema against its markdown documentation and reports:\n\n- **Missing documentation** — schema attributes or blocks with no corresponding doc entry\n- **Phantom documentation** — documented attributes that don't exist in the schema\n- **Ordering violations** — arguments or attributes not in alphabetical order\n- **Description style issues** — descriptions starting with articles or fluff words\n- **Misplaced computed attributes** — computed-only attributes in the Argument Reference section\n- **Heading style mismatches** — nested block headings not matching the preferred format\n- **Format / structure issues** — code blocks inside argument sections, multi-line attribute descriptions, interrupted attribute lists\n- **Label violations** — arguments missing (Required)/(Optional), or attributes that have them\n- **Byline mismatches** — section introductory paragraph doesn't match expected text\n- **Frontmatter problems** — missing required YAML fields, forbidden fields present, disallowed subcategories\n- **Title section problems** — missing title, wrong heading level, bad `\u003cKind\u003e: ` prefix\n- **Section presence** — required sections missing, forbidden sections present, timeouts section iff schema has timeouts\n- **Timeout mismatches** — documented timeout actions that don't exist in schema, or schema actions not documented\n- **Import section issues** — passive voice, missing code blocks, identity section validation\n- **Example section issues** — code blocks with disallowed languages, missing resource name\n- **Function signature** — missing signature code block, function name or parameters not present\n- **Region argument** — region-aware resources that don't document the `region` argument\n- **File size** — documentation files exceeding the 500KB Terraform Registry limit\n- **File extension** — wrong file extension for the layout (legacy vs registry)\n- **Link style** — reference-style link definitions that should be inline\n- **File alignment** — missing doc files, orphan doc files, mixed legacy/registry layouts\n\n## Installation\n\n```bash\ngo install github.com/YakDriver/swissshepherd@latest\n```\n\nRequires [Terraform](https://developer.hashicorp.com/terraform/install) in PATH when using `provider_dir` (for automatic schema generation).\n\n## Quick start\n\nCreate `.swissshepherd.hcl` in your provider repo root:\n\n```hcl\nprovider_source = \"registry.terraform.io/hashicorp/aws\"\nprovider_dir    = \".\"\n```\n\nThen run:\n\n```bash\nswissshepherd\n```\n\nThat's it. swissshepherd builds the provider, generates the schema, runs all checks, and reports findings.\n\n### Common invocations\n\n```bash\n# Check a single target (auto-detects type)\nswissshepherd --target aws_iam_role\n\n# Disambiguate when a name exists as both resource and data source\nswissshepherd --target aws_instance --type resource\n\n# Check all targets matching a prefix\nswissshepherd --prefix aws_dms_\n\n# Check all data sources\nswissshepherd --type data_source\n\n# Check a service's resources\nswissshepherd --prefix aws_s3_ --type resource\n```\n\n## CLI reference\n\n```\nswissshepherd [flags]\nswissshepherd check [flags]\n```\n\n| Flag | Description |\n|------|-------------|\n| `--config` | Config file path (default: `.swissshepherd.hcl`) |\n| `--schema-json` | Path to pre-generated `terraform providers schema -json` output |\n| `--provider-source` | Provider source address (e.g., `registry.terraform.io/hashicorp/aws`) |\n| `--provider-dir` | Path to provider source directory (triggers automatic build + schema generation) |\n| `--target` | Check a single named target |\n| `--type` | Restrict to a specific type (`resource`, `data_source`, `ephemeral`, `function`, `list_resource`, `action`) |\n| `--prefix` | Check all targets whose name begins with this prefix |\n| `--refresh-schema` | Regenerate cached schema even if `schema_json` file exists |\n| `--json` | Output results as JSON |\n| `--verbose` | Verbose logging (shows enabled checks, scoping, schema stats) |\n| `--version` | Print version and exit |\n\nCLI flags override config file values.\n\n### Output format\n\n```\nERROR  [rule_name] resource_name (path/to/file.md:42): message\nWARN   [rule_name] resource_name (path/to/file.md): message\n```\n\nExit code 0 = all checks passed. Exit code 1 = one or more errors found.\n\n## Configuration\n\nAll configuration lives in a single HCL file. The full reference:\n\n```hcl\n# .swissshepherd.hcl\n\n# ─── Provider identity ───────────────────────────────────────────────────────\n\nprovider_source = \"registry.terraform.io/hashicorp/aws\"\nprovider_dir    = \".\"\n\n# ─── Schema caching ─────────────────────────────────────────────────────────\n# When set, the schema is cached at this path (relative to provider_dir).\n# First run builds the provider and writes here; subsequent runs reuse it.\n# Use --refresh-schema to regenerate after provider code changes.\n\nschema_json = \"terraform-providers-schema/schema.json\"\n\n# ─── Global ignore lists ────────────────────────────────────────────────────\n\n# Suppress all schema+doc rule findings for deprecated/removed stubs.\n# File rules (frontmatter, file_check) still run.\nignore_contents_check      = [\"aws_kms_secret\"]\nignore_contents_check_file = \"website/ignore-contents-check.txt\"\n\n# Map schema names to doc file names when they differ.\n# Keys: plain \"name\" (all types) or \"type/name\" (scoped).\nfile_aliases = {\n  \"list_resource/aws_ebs_volume\" = \"aws_ec2_ebs_volume\"\n  \"aws_alb\"                      = \"aws_lb\"\n}\n\n# ─── Check blocks ───────────────────────────────────────────────────────────\n# Each check block configures one rule. All rules are enabled by default.\n# Add `enabled = false` to disable. See \"Rules reference\" for details.\n```\n\n### Path resolution\n\nAll relative paths in the config (`provider_dir`, `schema_json`, `*_file` options) resolve against the **current working directory** — the same convention as `terraform`, `go`, and `make`. The config file's location has no effect.\n\nIn practice: `cd` into the provider repo root and run swissshepherd from there:\n\n```bash\ncd terraform-provider-aws\nswissshepherd --config .ci/swissshepherd.hcl\n```\n\n### List files\n\nOptions ending in `_file` (`ignore_targets_file`, `allow_subcategories_file`, `ignore_missing_file`, etc.) read one entry per line. Empty lines and lines starting with `#` are ignored.\n\n## Rules reference\n\n### Overview\n\n| Rule | Kind | Description |\n|------|------|-------------|\n| `example_section` | per-target | Example code block validation |\n| `file_check` | per-file | File size, extension, and link style validation |\n| `file_match` | global | File↔schema alignment: missing docs, orphan files, mixed layouts |\n| `frontmatter` | per-file | YAML frontmatter field validation |\n| `import_section` | per-target | Import section style and structure |\n| `region_argument` | per-target | Region argument presence for region-aware types |\n| `schema_docs` | per-target | Schema coverage, ordering, description style, heading style, format, labels, and bylines |\n| `section_presence` | per-target | Required/forbidden section enforcement |\n| `signature_section` | per-target | Function signature validation |\n| `timeouts_section` | per-target | Timeout actions match schema bidirectionally |\n| `title_section` | per-target | Level-1 heading validation |\n\n**Per-target** rules run once per (resource, data source, etc.) and compare schema against parsed markdown. **Per-file** rules run on raw file bytes. **Global** rules run once per invocation.\n\n---\n\n### `example_section`\n\nValidates example code blocks:\n- Code block languages are in the allowed list\n- Code blocks contain the resource/data source name\n\n```hcl\ncheck \"example_section\" {\n  allow_languages = [\"terraform\", \"hcl\"]  # default\n}\n```\n\n---\n\n### `file_check`\n\nValidates file-level properties.\n\n```hcl\ncheck \"file_check\" {\n  max_file_size             = 500000                                # bytes (default: 500KB registry limit)\n  allow_extensions          = [\".md\", \".html.markdown\", \".html.md\"] # accepted extensions\n  allow_registry_extensions = [\".md\"]                               # stricter set for docs/ layout\n  inline_links              = true                                  # flag reference-style [ref]: url links\n}\n```\n\nWhen `inline_links` is enabled, reference-style link definitions (`[label]: url`) are flagged with a suggestion to use inline `[text](url)` style instead.\n\n---\n\n### `file_match`\n\nValidates file↔schema alignment (runs once per invocation):\n- **require_doc**: every schema resource must have a documentation file\n- **require_schema**: every documentation file must have a matching schema resource\n- **mixed_layout**: can't mix legacy (`website/docs/`) and registry (`docs/`) layouts\n\n```hcl\ncheck \"file_match\" {\n  require_doc    = true   # default: true\n  require_schema = true   # default: true\n  mixed_layout   = true   # default: true\n\n  ignore_missing      = [\"aws_alb\", \"aws_alb_listener\"]       # no doc required\n  ignore_missing_file = \"website/ignore-file-missing.txt\"\n  ignore_extra        = [\"aws_removed_thing\"]                  # orphan doc OK\n  ignore_extra_file   = \"website/ignore-file-mismatch.txt\"\n}\n```\n\nThe `ignore_missing` and `ignore_extra` lists suppress findings for specific targets. Use `ignore_missing_file` / `ignore_extra_file` for file-based lists.\n\n---\n\n### `frontmatter`\n\nValidates YAML frontmatter at the top of each doc file. Requirements can come from both the check block (global) and the type block (per-type via `frontmatter_require` / `frontmatter_forbid`). Both sources are merged — a field required by either is enforced.\n\n```hcl\ncheck \"frontmatter\" {\n  # Require fields to be present\n  require_subcategory = true\n  require_page_title  = true\n  require_description = true\n  require_layout      = false  # legacy layout only\n\n  # Forbid fields from being present\n  forbid_subcategory    = false\n  forbid_page_title     = false\n  forbid_description    = false\n  forbid_layout         = true   # registry layout\n  forbid_sidebar_current = true  # always forbidden in modern docs\n\n  # Subcategory allowlist (empty = anything goes)\n  allow_subcategories      = [\"S3\", \"VPC\", \"IAM\"]\n  allow_subcategories_file = \"website/allowed-subcategories.txt\"\n\n  # Targets where subcategory: \"\" is acceptable\n  allow_empty_subcategory_targets = [\"arn_build\", \"arn_parse\"]\n}\n```\n\n---\n\n### `import_section`\n\nValidates import section content and structure:\n- No passive voice (\"can be imported\" → use imperative)\n- No \"e.g.\" (use \"For example\" or just show the example)\n- Code blocks present with correct types (`terraform` and `console`)\n- Correct ordering (terraform block before console block)\n- Identity-aware: when resource has identity schema, validates `### Identity Schema` subsection\n\n```hcl\ncheck \"import_section\" {\n  require_identity_section = true  # default: true\n}\n```\n\n---\n\n### `region_argument`\n\nValidates that region-aware resources document the `region` argument. Only fires for types with `region_aware = true` in their type block (resources, data sources, ephemerals by default).\n\n```hcl\ncheck \"region_argument\" {\n  ignore_resources      = [\"aws_global_accelerator\"]\n  ignore_resources_file = \"website/ignore-region.txt\"\n}\n```\n\n---\n\n### `schema_docs`\n\nThe primary rule. Validates argument and attribute documentation against the provider schema.\n\n**Sub-checks** (all enabled by default; disable individually):\n\n| Sub-check | What it validates |\n|-----------|-------------------|\n| `byline` | First paragraph after section heading matches expected byline text (from type) |\n| `coverage` | Every schema attr is documented; every documented attr exists in schema |\n| `description` | Descriptions don't start with bad prefixes (\"A \", \"The \", \"Specifies \", etc.) |\n| `format` | No code blocks in arg/attr sections; single-line attrs; uninterrupted lists |\n| `heading` | Block headings match the preferred template style |\n| `labels` | Arguments have (Required)/(Optional); attributes do not |\n| `ordering` | Attributes are alphabetical within required/optional groups |\n\n**Config:**\n\n```hcl\ncheck \"schema_docs\" {\n  # Sub-check toggles\n  byline      = true\n  coverage    = true\n  description = true\n  format      = true\n  heading     = true\n  labels      = true\n  ordering    = true\n\n  # Heading templates (see \"Heading templates\" section)\n  block_heading_styles           = [\"`{Block}` Block\", \"{Block}\", \"{Title}\"]\n  prefer_block_heading_styles = [\"`{Block}` Block\"]\n\n  # Coverage options\n  ignore_deprecated   = true                     # skip deprecated schema attrs\n  implicit_attributes = [\"id\", \"tags_all\"]       # never flagged as undocumented\n  allow_phantoms   = [\"tags\", \"tags_all\"]     # never flagged as phantom\n  skip_blocks         = [\"timeouts\"]             # blocks skipped entirely\n\n  # Description options\n  bad_prefixes = [\"A \", \"An \", \"The \", \"Specifies \"]\n\n  # Format options\n  no_code_blocks              = true   # no fenced code blocks in arg/attr sections\n  single_line_attrs           = true   # each attribute on one line\n  uninterrupted_lists         = true   # no paragraphs between list items\n  allow_attribute_indentation = true   # allow indented sub-attributes in Attribute Reference (default: true)\n}\n```\n\n---\n\n### `section_presence`\n\nEnforces that required sections exist and forbidden sections are absent. Requirements come from the `type` block:\n\n- `require_attributes` = `\"required\"` / `\"optional\"` / `\"forbidden\"`\n- `require_import` = same\n- `require_timeouts` = same (overridden by schema: if schema has timeouts block, section is required regardless)\n- `require_signature` = same\n\nNo check-level config — entirely driven by type definitions.\n\n---\n\n### `signature_section`\n\nValidates function signature documentation:\n- Signature code block is present\n- Contains the function name\n- Contains all schema parameter names\n\nNo config options.\n\n---\n\n### `timeouts_section`\n\nValidates that documented timeout actions match the schema bidirectionally:\n- Every schema timeout action must be documented\n- Every documented timeout action must exist in the schema\n\nNo config options.\n\n---\n\n### `title_section`\n\nValidates the level-1 heading (`# Resource: aws_thing`).\n\nChecks:\n- Heading exists and is at level 1\n- Begins with an allowed `\u003cKind\u003e: ` prefix (from `allow_prefixes` or the type's `title_prefix`)\n- No code blocks appear above the first `##` heading\n\n```hcl\ncheck \"title_section\" {\n  allow_prefixes = [\"Resource\", \"Data Source\", \"Ephemeral\", \"Function\"]\n}\n```\n\n## Path scoping\n\nEvery `check` block accepts path-scoping fields that control which targets the rule runs against. This is the primary mechanism for incremental rollout across a large provider.\n\n```hcl\ncheck \"schema_docs\" {\n  # Type axis: only these type names (empty = all)\n  types = [\"resource\", \"data_source\"]\n\n  # Name axis (OR'd together): prefix match OR exact match\n  prefixes = [\"aws_s3\", \"aws_appflow\"]\n  targets  = [\"aws_instance\"]\n\n  # Deny list: always excluded, even when allowlists match\n  ignore_targets      = [\"aws_legacy_resource\"]\n  ignore_targets_file = \"website/ignore-ordering.txt\"\n  ignore_prefixes     = [\"aws_appstream\"]\n}\n```\n\nAll four name-axis fields (`prefixes`, `targets`, `ignore_targets`, `ignore_prefixes`) support **type/name notation** for type-scoped entries:\n\n```hcl\ncheck \"schema_docs\" {\n  # Only match aws_thing when it's a data source, not a resource\n  ignore_targets = [\"data_source/aws_thing\"]\n\n  # Only match aws_s3_ prefixed data sources\n  prefixes = [\"data_source/aws_s3_\"]\n}\n```\n\nPlain entries (no `/`) match any type. Qualified entries (`type/name` or `type/prefix`) only match when the type matches.\n\n**Evaluation order:**\n\n1. `ignore_targets` / `ignore_prefixes` — if matched, target is excluded unconditionally\n2. `types` — if non-empty, target's type must be in the list\n3. `prefixes` / `targets` — if either is non-empty, target must match at least one; both empty = all names pass\n\n## Type system\n\nswissshepherd ships with built-in type definitions for all standard Terraform documentation categories. Override any default or add new types in your config:\n\n### Built-in types\n\n| Type | Schema kind | Default doc path | Region-aware |\n|------|-------------|------------------|--------------|\n| `resource` | `resource` | `website/docs/r/{name}.html.markdown` | yes |\n| `data_source` | `data_source` | `website/docs/d/{name}.html.markdown` | yes |\n| `ephemeral` | `ephemeral` | `website/docs/ephemeral-resources/{name}.html.markdown` | yes |\n| `function` | `function` | `website/docs/functions/{name}.html.markdown` | no |\n| `list_resource` | `list_resource` | `website/docs/list-resources/{name}.html.markdown` | yes |\n| `action` | `action` | `website/docs/actions/{name}.html.markdown` | no |\n| `guide` | `none` | `website/docs/guides/{name}.html.markdown` | no |\n| `index` | `none` | `website/docs/index.html.markdown` | no |\n\n### Overriding a type\n\n```hcl\ntype \"resource\" {\n  schema_kind   = \"resource\"\n  website_paths = [\n    \"docs/resources/{name}.md\",\n    \"website/docs/r/{name}.html.markdown\",\n  ]\n  title_prefix       = \"Resource\"\n  require_attributes = \"required\"\n  require_import     = \"optional\"\n  require_timeouts   = \"optional\"\n  require_signature  = \"forbidden\"\n  region_aware       = true\n\n  frontmatter_require = [\"description\", \"page_title\"]\n  frontmatter_forbid  = [\"sidebar_current\"]\n\n  arguments_bylines = [\n    \"This resource supports the following arguments:\",\n    \"The following arguments are required:\",\n    \"The following arguments are optional:\",\n  ]\n  attributes_bylines = [\n    \"This resource exports the following attributes in addition to the arguments above:\",\n  ]\n}\n```\n\n### Type fields\n\n| Field | Description |\n|-------|-------------|\n| `schema_kind` | Schema accessor: `resource`, `data_source`, `ephemeral`, `function`, `list_resource`, `action`, `none` |\n| `website_paths` | Ordered list of path templates. `{name}` = provider-prefix-stripped target name |\n| `title_prefix` | Expected `# \u003cPrefix\u003e: ` in the level-1 heading |\n| `arguments_heading` | Override the expected heading text (default: `\"Argument Reference\"`) |\n| `arguments_bylines` | Allowed paragraph texts under `## Argument Reference` |\n| `attributes_bylines` | Allowed paragraph texts under `## Attribute Reference` |\n| `allow_missing_arguments_byline` | Don't require a byline paragraph |\n| `require_attributes` | `\"required\"` / `\"optional\"` / `\"forbidden\"` |\n| `require_import` | Same |\n| `require_timeouts` | Same (overridden by schema when timeouts block exists) |\n| `require_signature` | Same |\n| `frontmatter_require` | Fields that must be present in YAML frontmatter |\n| `frontmatter_forbid` | Fields that must be absent |\n| `region_aware` | Whether `region_argument` rule applies to this type |\n\n## Heading templates\n\nThe `block_heading_styles` list controls which `###` heading formats are recognized as block documentation within argument/attribute sections.\n\n### Placeholders\n\n| Placeholder | Matches | Example | Extracted name |\n|-------------|---------|---------|----------------|\n| `{Block}` | snake_case identifier | `network_interface` | `network_interface` |\n| `{Title}` | Title Case words (→ snake_case) | `Network Interface` | `network_interface` |\n\n### Examples\n\n| Template | Matches heading | Extracted |\n|----------|-----------------|-----------|\n| `` `{Block}` Block `` | `` `network` Block `` | `network` |\n| `{Block} Configuration Block` | `vpc_config Configuration Block` | `vpc_config` |\n| `{Block} Argument Reference` | `filter Argument Reference` | `filter` |\n| `{Title}` | `Credit Specification` | `credit_specification` |\n\nNote: goldmark strips backticks from inline code in headings, so `` ### `network` Block `` becomes `network Block` in parsed text.\n\n### Preferred style\n\nSet `prefer_block_heading_styles` to emit warnings when a heading matches an accepted style but not the preferred one:\n\n```hcl\ncheck \"schema_docs\" {\n  block_heading_styles           = [\"`{Block}` Block\", \"{Block} Block\", \"{Block}\", \"{Title}\"]\n  prefer_block_heading_styles = [\"`{Block}` Block\"]\n}\n```\n\n## Schema generation\n\nWhen `provider_dir` is set, swissshepherd automatically:\n\n1. Builds the provider with `go build`\n2. Creates a temporary Terraform plugin directory\n3. Runs `terraform init` + `terraform providers schema -json`\n4. Uses the generated schema for all checks\n5. Cleans up temporary files\n\n### Caching\n\nFor large providers where the build takes minutes, set `schema_json` to cache:\n\n```hcl\nprovider_dir = \".\"\nschema_json  = \"terraform-providers-schema/schema.json\"\n```\n\nFirst run builds and caches. Subsequent runs reuse the file instantly. Regenerate with `--refresh-schema` or delete the cached file.\n\n### Pre-generated schema\n\nSkip the build entirely by providing a pre-generated schema:\n\n```bash\nswissshepherd --schema-json path/to/schema.json --provider-source registry.terraform.io/hashicorp/aws\n```\n\n## Design\n\n- **Schema is the source of truth** — derived from `terraform providers schema -json`, not Go source parsing\n- **Markdown parsed with goldmark** — full AST, not regex\n- **Two rule interfaces**: `Rule` (schema + AST) and `FileRule` (raw bytes). The runner reads each file once and feeds it to both\n- **Type-driven architecture** — documentation categories are config, not code. Adding a new Terraform category is a config change\n- **Per-check path scoping** — each rule rolls out independently via `types`, `prefixes`, `targets`, `ignore_targets`, `ignore_prefixes`\n- **Config-driven** — HCL configuration via [hcl/v2](https://github.com/hashicorp/hcl); no magic values\n- **Global checks** run once per invocation (directory layout, file mismatch)\n- **YAML frontmatter** parsed with [yaml.v3](https://gopkg.in/yaml.v3)\n\n## License\n\nMPL-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyakdriver%2Fswissshepherd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyakdriver%2Fswissshepherd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyakdriver%2Fswissshepherd/lists"}