@gspothq/cli 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/README.md +18 -2
  2. package/configurations/database/postgres/manifest.toml +114 -0
  3. package/configurations/database/postgres/rules/POSTGRES.md +78 -0
  4. package/configurations/framework/astro/eslint-presets.json +138 -0
  5. package/configurations/framework/astro/manifest.toml +95 -0
  6. package/configurations/framework/astro/rules/ASTRO.md +39 -0
  7. package/configurations/framework/expo/manifest.toml +43 -0
  8. package/configurations/framework/express/manifest.toml +17 -0
  9. package/configurations/framework/express/rules/EXPRESS.md +73 -0
  10. package/configurations/framework/express/semgrep/express.yml +71 -0
  11. package/configurations/framework/fastapi/manifest.toml +11 -0
  12. package/configurations/framework/fastapi/rules/FASTAPI.md +100 -0
  13. package/configurations/framework/fastapi/semgrep/fastapi.yml +44 -0
  14. package/configurations/framework/nestjs/eslint-presets.json +62 -0
  15. package/configurations/framework/nestjs/manifest.toml +52 -0
  16. package/configurations/framework/nestjs/rules/NESTJS.md +44 -0
  17. package/configurations/framework/nextjs/eslint-presets.json +73 -0
  18. package/configurations/framework/nextjs/manifest.toml +119 -0
  19. package/configurations/framework/nextjs/rules/NEXTJS.md +43 -0
  20. package/configurations/framework/react/eslint-presets.json +77 -0
  21. package/configurations/framework/react/manifest.toml +93 -0
  22. package/configurations/framework/react/rules/REACT.md +108 -0
  23. package/configurations/framework/react-dom/eslint-presets.json +110 -0
  24. package/configurations/framework/react-dom/manifest.toml +24 -0
  25. package/configurations/framework/react-native/manifest.toml +46 -0
  26. package/configurations/framework/react-native/rules/REACT-NATIVE.md +51 -0
  27. package/configurations/framework/site/manifest.toml +170 -0
  28. package/configurations/framework/site/rules/SITE.md +20 -0
  29. package/configurations/framework/svelte/eslint-presets.json +67 -0
  30. package/configurations/framework/svelte/manifest.toml +83 -0
  31. package/configurations/framework/svelte/rules/SVELTE.md +48 -0
  32. package/configurations/framework/vue/eslint-presets.json +180 -0
  33. package/configurations/framework/vue/manifest.toml +83 -0
  34. package/configurations/framework/vue/rules/VUE.md +41 -0
  35. package/configurations/general/commits/manifest.toml +111 -0
  36. package/configurations/general/commits/rules/GIT.md +31 -0
  37. package/configurations/general/coverage/manifest.toml +3 -0
  38. package/configurations/general/dependencies/manifest.toml +159 -0
  39. package/configurations/general/dependencies/rules/DEPENDENCIES.md +30 -0
  40. package/configurations/general/dependencies/syncpack.json +15 -0
  41. package/configurations/general/docs/manifest.toml +79 -0
  42. package/configurations/general/docs/rules/DOCS.md +86 -0
  43. package/configurations/general/duplication/manifest.toml +32 -0
  44. package/configurations/general/engineering/manifest.toml +8 -0
  45. package/configurations/general/engineering/rules/agent/PLANNING.md +42 -0
  46. package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +16 -0
  47. package/configurations/general/engineering/rules/agent/TALKING.md +3 -0
  48. package/configurations/general/engineering/rules/agent/TASKS.md +19 -0
  49. package/configurations/general/engineering/rules/agent/WORKING.md +83 -0
  50. package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +32 -0
  51. package/configurations/general/engineering/rules/code/CLI.md +37 -0
  52. package/configurations/general/engineering/rules/code/COMMENTS.md +38 -0
  53. package/configurations/general/engineering/rules/code/CONFIGURATION.md +25 -0
  54. package/configurations/general/engineering/rules/code/ERRORS.md +49 -0
  55. package/configurations/general/engineering/rules/code/HTTP.md +46 -0
  56. package/configurations/general/engineering/rules/code/LOGGING.md +55 -0
  57. package/configurations/general/engineering/rules/code/TESTING.md +58 -0
  58. package/configurations/general/files/manifest.toml +141 -0
  59. package/configurations/general/files/rules/YAML.md +19 -0
  60. package/configurations/general/format/manifest.toml +152 -0
  61. package/configurations/general/gspot/manifest.toml +65 -0
  62. package/configurations/general/gspot/rules/GENERATED.md +10 -0
  63. package/configurations/general/licenses/manifest.toml +33 -0
  64. package/configurations/general/naming/defaults.json +423 -0
  65. package/configurations/general/naming/manifest.toml +35 -0
  66. package/configurations/general/naming/rules/NAMING.md +179 -0
  67. package/configurations/general/prose/manifest.toml +86 -0
  68. package/configurations/general/prose/rules/WRITING.md +31 -0
  69. package/configurations/general/prose/vale/styles/LICENSES.md +55 -0
  70. package/configurations/general/prose/vale/styles/gspot/ampm.yml +8 -0
  71. package/configurations/general/prose/vale/styles/gspot/conscious-language.yml +16 -0
  72. package/configurations/general/prose/vale/styles/gspot/contractions.yml +43 -0
  73. package/configurations/general/prose/vale/styles/gspot/currency.yml +8 -0
  74. package/configurations/general/prose/vale/styles/gspot/dashes.yml +8 -0
  75. package/configurations/general/prose/vale/styles/gspot/date-ordinals.yml +41 -0
  76. package/configurations/general/prose/vale/styles/gspot/dates.yml +7 -0
  77. package/configurations/general/prose/vale/styles/gspot/file-paths.yml +7 -0
  78. package/configurations/general/prose/vale/styles/gspot/future.yml +17 -0
  79. package/configurations/general/prose/vale/styles/gspot/hedging.yml +22 -0
  80. package/configurations/general/prose/vale/styles/gspot/history.yml +16 -0
  81. package/configurations/general/prose/vale/styles/gspot/idioms.yml +21 -0
  82. package/configurations/general/prose/vale/styles/gspot/interface-verbs.yml +11 -0
  83. package/configurations/general/prose/vale/styles/gspot/jargon.yml +11 -0
  84. package/configurations/general/prose/vale/styles/gspot/leading-conjunctions.yml +12 -0
  85. package/configurations/general/prose/vale/styles/gspot/link-text.yml +8 -0
  86. package/configurations/general/prose/vale/styles/gspot/locations.yml +14 -0
  87. package/configurations/general/prose/vale/styles/gspot/marketing.yml +26 -0
  88. package/configurations/general/prose/vale/styles/gspot/merge-conflict-markers.yml +13 -0
  89. package/configurations/general/prose/vale/styles/gspot/militaristic-language.yml +22 -0
  90. package/configurations/general/prose/vale/styles/gspot/modals.yml +9 -0
  91. package/configurations/general/prose/vale/styles/gspot/numbered-units.yml +18 -0
  92. package/configurations/general/prose/vale/styles/gspot/obvious-fields.yml +14 -0
  93. package/configurations/general/prose/vale/styles/gspot/ordinal-adverbs.yml +15 -0
  94. package/configurations/general/prose/vale/styles/gspot/percentage-numbers.yml +8 -0
  95. package/configurations/general/prose/vale/styles/gspot/placeholders.yml +7 -0
  96. package/configurations/general/prose/vale/styles/gspot/product-centric-writing.yml +8 -0
  97. package/configurations/general/prose/vale/styles/gspot/punctuated-headings.yml +11 -0
  98. package/configurations/general/prose/vale/styles/gspot/self-reference.yml +15 -0
  99. package/configurations/general/prose/vale/styles/gspot/session-identifiers.yml +11 -0
  100. package/configurations/general/prose/vale/styles/gspot/since.yml +6 -0
  101. package/configurations/general/prose/vale/styles/gspot/straight-quotes.yml +11 -0
  102. package/configurations/general/prose/vale/styles/gspot/suspended-hyphens.yml +8 -0
  103. package/configurations/general/prose/vale/styles/gspot/symbols.yml +8 -0
  104. package/configurations/general/prose/vale/styles/gspot/time-ranges.yml +14 -0
  105. package/configurations/general/prose/vale/styles/gspot/url-prepositions.yml +10 -0
  106. package/configurations/general/prose/vale/styles/gspot/version-range.yml +8 -0
  107. package/configurations/general/repository/manifest.toml +24 -0
  108. package/configurations/general/secrets/gitleaks.toml +2 -0
  109. package/configurations/general/secrets/manifest.toml +125 -0
  110. package/configurations/general/secrets/rules/SECRETS.md +13 -0
  111. package/configurations/general/security/manifest.toml +77 -0
  112. package/configurations/general/security/rules/SECURITY.md +55 -0
  113. package/configurations/general/spelling/manifest.toml +46 -0
  114. package/configurations/general/structure/manifest.toml +70 -0
  115. package/configurations/infrastructure/ansible/manifest.toml +33 -0
  116. package/configurations/infrastructure/docker/manifest.toml +127 -0
  117. package/configurations/infrastructure/docker/rules/DOCKER.md +135 -0
  118. package/configurations/infrastructure/docker/trivyignore +2 -0
  119. package/configurations/infrastructure/github-actions/manifest.toml +76 -0
  120. package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +43 -0
  121. package/configurations/infrastructure/github-actions/zizmor.yml +3 -0
  122. package/configurations/infrastructure/nginx/manifest.toml +51 -0
  123. package/configurations/infrastructure/nginx/rules/NGINX.md +38 -0
  124. package/configurations/infrastructure/openapi/manifest.toml +50 -0
  125. package/configurations/infrastructure/openapi/rules/OPENAPI.md +39 -0
  126. package/configurations/infrastructure/xcode/manifest.toml +131 -0
  127. package/configurations/infrastructure/xcode/rules/XCODE.md +34 -0
  128. package/configurations/language/bash/manifest.toml +261 -0
  129. package/configurations/language/bash/rules/BASH.md +106 -0
  130. package/configurations/language/bash/rules/LANGUAGE.md +213 -0
  131. package/configurations/language/bash/rules/NAMING.md +62 -0
  132. package/configurations/language/bash/rules/OPERATIONS.md +77 -0
  133. package/configurations/language/bash/rules/SAFETY.md +222 -0
  134. package/configurations/language/bash/semgrep/bash.yml +30 -0
  135. package/configurations/language/css/manifest.toml +98 -0
  136. package/configurations/language/css/rules/CSS.md +28 -0
  137. package/configurations/language/css/rules/NAMING.md +17 -0
  138. package/configurations/language/css/rules/TAILWIND.md +26 -0
  139. package/configurations/language/css/stylelint-rule-names.json +89 -0
  140. package/configurations/language/html/manifest.toml +59 -0
  141. package/configurations/language/html/rules/HTML.md +27 -0
  142. package/configurations/language/html/rules/NAMING.md +16 -0
  143. package/configurations/language/javascript/eslint-presets.json +997 -0
  144. package/configurations/language/javascript/eslint-rule-names.json +299 -0
  145. package/configurations/language/javascript/eslint-runtime-names.json +61 -0
  146. package/configurations/language/javascript/manifest.toml +393 -0
  147. package/configurations/language/javascript/rules/BUN.md +13 -0
  148. package/configurations/language/javascript/rules/COMPONENTS.md +18 -0
  149. package/configurations/language/javascript/rules/DENO.md +15 -0
  150. package/configurations/language/javascript/rules/JAVASCRIPT.md +60 -0
  151. package/configurations/language/javascript/rules/NAMING.md +31 -0
  152. package/configurations/language/javascript/rules/NODE.md +21 -0
  153. package/configurations/language/javascript/rules/PLAYWRIGHT.md +34 -0
  154. package/configurations/language/javascript/semgrep/javascript.yml +84 -0
  155. package/configurations/language/markdown/manifest.toml +85 -0
  156. package/configurations/language/markdown/markdownlint-cli2.mjs +19 -0
  157. package/configurations/language/python/manifest.toml +621 -0
  158. package/configurations/language/python/rules/DESIGN.md +136 -0
  159. package/configurations/language/python/rules/FLOW.md +124 -0
  160. package/configurations/language/python/rules/NAMING.md +53 -0
  161. package/configurations/language/python/rules/PACKAGING.md +49 -0
  162. package/configurations/language/python/rules/PYTHON.md +136 -0
  163. package/configurations/language/python/rules/TYPING.md +132 -0
  164. package/configurations/language/sql/manifest.toml +68 -0
  165. package/configurations/language/sql/rules/NAMING.md +90 -0
  166. package/configurations/language/sql/rules/SQL.md +9 -0
  167. package/configurations/language/swift/manifest.toml +164 -0
  168. package/configurations/language/swift/rules/NAMING.md +88 -0
  169. package/configurations/language/swift/rules/SWIFT.md +134 -0
  170. package/configurations/language/swift/rules/SWIFTUI.md +61 -0
  171. package/configurations/language/swift/rules/UIKIT.md +99 -0
  172. package/configurations/language/swift/semgrep/swift.yml +126 -0
  173. package/configurations/language/typescript/eslint-presets.json +215 -0
  174. package/configurations/language/typescript/manifest.toml +72 -0
  175. package/configurations/language/typescript/rules/NAMING.md +73 -0
  176. package/configurations/language/typescript/rules/TYPESCRIPT.md +79 -0
  177. package/configurations/library/drizzle/manifest.toml +68 -0
  178. package/configurations/library/drizzle/rules/DRIZZLE.md +44 -0
  179. package/configurations/library/react-hook-form/manifest.toml +15 -0
  180. package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +48 -0
  181. package/configurations/library/swift-snapshot-testing/manifest.toml +32 -0
  182. package/configurations/library/tanstack-query/eslint-presets.json +21 -0
  183. package/configurations/library/tanstack-query/manifest.toml +26 -0
  184. package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +41 -0
  185. package/configurations/library/testing-library/eslint-presets.json +191 -0
  186. package/configurations/library/testing-library/manifest.toml +47 -0
  187. package/configurations/library/translations/manifest.toml +40 -0
  188. package/configurations/library/translations/rules/NEXT-INTL.md +33 -0
  189. package/configurations/library/translations/rules/TRANSLATIONS.md +102 -0
  190. package/configurations/library/trpc/manifest.toml +14 -0
  191. package/configurations/library/trpc/rules/TRPC.md +43 -0
  192. package/configurations/library/zod/manifest.toml +25 -0
  193. package/configurations/library/zod/rules/ZOD.md +44 -0
  194. package/configurations/library/zustand/manifest.toml +18 -0
  195. package/configurations/library/zustand/rules/ZUSTAND.md +106 -0
  196. package/configurations/platform/cloudflare/manifest.toml +94 -0
  197. package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +23 -0
  198. package/configurations/platform/cloudflare/semgrep/cloudflare.yml +12 -0
  199. package/configurations/platform/supabase/manifest.toml +112 -0
  200. package/configurations/platform/supabase/rules/SUPABASE.md +129 -0
  201. package/configurations/platform/supabase/semgrep/supabase.yml +60 -0
  202. package/configurations/testing/jest/manifest.toml +54 -0
  203. package/configurations/testing/pytest/manifest.toml +53 -0
  204. package/configurations/testing/swift-tests/manifest.toml +52 -0
  205. package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +79 -0
  206. package/configurations/testing/vitest/eslint-presets.json +22 -0
  207. package/configurations/testing/vitest/manifest.toml +69 -0
  208. package/configurations/testing/vitest/rules/VITEST.md +18 -0
  209. package/dist/LICENSE.md +201 -0
  210. package/dist/THIRD-PARTY-NOTICES.md +25 -0
  211. package/dist/eslint-rule-levels.json +10 -0
  212. package/dist/gspot.js +30740 -0
  213. package/dist/linkinator-program.js +65 -0
  214. package/dist/purgecss-program.js +51 -0
  215. package/dist/tool-versions.json +3 -0
  216. package/grammars/bash.wasm +0 -0
  217. package/grammars/css.wasm +0 -0
  218. package/grammars/html.wasm +0 -0
  219. package/grammars/javascript.wasm +0 -0
  220. package/grammars/licenses/tree-sitter-bash.txt +21 -0
  221. package/grammars/licenses/tree-sitter-css.txt +21 -0
  222. package/grammars/licenses/tree-sitter-html.txt +21 -0
  223. package/grammars/licenses/tree-sitter-javascript.txt +21 -0
  224. package/grammars/licenses/tree-sitter-python.txt +21 -0
  225. package/grammars/licenses/tree-sitter-swift.txt +21 -0
  226. package/grammars/licenses/tree-sitter-typescript.txt +21 -0
  227. package/grammars/python.wasm +0 -0
  228. package/grammars/swift.wasm +0 -0
  229. package/grammars/tsx.wasm +0 -0
  230. package/grammars/typescript.wasm +0 -0
  231. package/gspot.schema.json +45991 -0
  232. package/package.json +93 -5
@@ -0,0 +1,70 @@
1
+ [configuration]
2
+ title = "Structure"
3
+ always_selected = true
4
+ description = "The structural rules no standard linter ships, over every language: wrappers, barrels, lone files, and the size limits."
5
+
6
+ [files]
7
+ languages = true
8
+
9
+ [[check]]
10
+ name = "lone-files"
11
+ example = "With the TypeScript configuration at level `all`, `feature/only.ts` reports `lone-file` when it is the folder's only code file. Move it beside related files and update references, or keep the folder when a second substantial file belongs to that feature. Adding `feature/second.ts` makes this folder contain two code files."
12
+ runs = "scope"
13
+ level = "all"
14
+ stage = "commit"
15
+ summary = "Finds a folder that holds one code file and nothing else."
16
+ why = "A folder of one file adds a level to every path and promises siblings that never arrive."
17
+ help = "Move the file up beside its neighbors, or use gspot ignore structure/lone-files with paths and a reason."
18
+
19
+ [[check]]
20
+ name = "shared-prefixes"
21
+ example = "Sibling files `asset-card.ts`, `asset-list.ts`, and `asset-row.ts` share a repeated prefix. Move them into `asset/` with names `card.ts`, `list.ts`, and `row.ts`, then update imports."
22
+ runs = "scope"
23
+ level = "all"
24
+ stage = "commit"
25
+ summary = "Finds sibling files that share a name prefix, like asset-card, asset-list and asset-row in one folder."
26
+ why = "Files that share a prefix are one concept split by suffix; they belong in a folder named after the prefix."
27
+ help = "Move the files into a folder named after the shared prefix, or allow the set with a reason."
28
+
29
+ [[check]]
30
+ name = "file-folder-clashes"
31
+ example = "Sibling entries `report.ts` and `report/` have the same stem and report a collision. Rename the file or directory after its distinct responsibility and update every reference."
32
+ runs = "scope"
33
+ level = "all"
34
+ stage = "commit"
35
+ summary = "Finds a file and a folder in the same place with the same stem, like turn.ts beside turn/."
36
+ why = "An import of ./turn names both, and a reader cannot tell which one loads."
37
+ help = "Rename the file or the folder so their stems differ."
38
+
39
+ [[check]]
40
+ name = "file-lines"
41
+ example = "A Python, Bash, or SQL source file above its language's code-line ceiling reports its count. Split independent responsibilities into their owners and rerun the check."
42
+ level = "all"
43
+ stage = "commit"
44
+ runs = "scope"
45
+ files = { extensions = [".sh", ".bash", ".zsh", ".bats", ".py", ".pyi", ".sql", ".pgsql", ".psql"], tags = ["shebang:shell"], kinds = ["source"] }
46
+ summary = "Checks source code lines against each language's ceiling."
47
+ why = "A long file hides independent responsibilities from the reader."
48
+ help = "Split the file by its responsibility, or raise its limits.`<language>`.file_lines with a reason."
49
+
50
+ [[check]]
51
+ name = "trivial-functions"
52
+ example = "A Bash function that only calls another function reports its declaration. Inline that call into its callers and remove the forwarding function."
53
+ level = "all"
54
+ stage = "commit"
55
+ runs = "scope"
56
+ files = { extensions = [".py", ".swift", ".sh", ".bash", ".zsh", ".bats"], tags = ["shell"], kinds = ["source"] }
57
+ summary = "Finds functions with too few statements and files that only forward to other code."
58
+ why = "A function of one statement makes the reader jump for nothing."
59
+ help = "Inline a forwarding function at its calls and delete it. Split independent statements into the files that use them."
60
+
61
+ [[check]]
62
+ name = "env-reads"
63
+ example = "A Python file outside architecture.roles.environment_readers that reads os.environ reports that read. Move it into a declared environment reader and pass its value to callers."
64
+ level = "all"
65
+ stage = "commit"
66
+ runs = "scope"
67
+ files = { extensions = [".py", ".swift", ".sh", ".bash", ".zsh", ".bats"], tags = ["shell"], kinds = ["source"] }
68
+ summary = "Finds environment reads outside the files listed in architecture.roles.environment_readers."
69
+ why = "Environment reads spread through the code hide which settings a program needs."
70
+ help = "Move the environment read into a file listed in architecture.roles.environment_readers, then pass the value to callers."
@@ -0,0 +1,33 @@
1
+ [configuration]
2
+ title = "Ansible"
3
+ description = "Ansible playbooks, roles, and collections checked by ansible-lint."
4
+
5
+ [files]
6
+ paths = ["**/ansible.cfg", "**/galaxy.yml", "**/requirements.yml", "**/roles/*/tasks/main.yml"]
7
+
8
+ [[tool]]
9
+ name = "ansible-lint"
10
+ version = "26.8.0"
11
+ min_version = "24.0.0"
12
+ pypi = "ansible-lint"
13
+ mise = "pipx:ansible-lint"
14
+ brew = "ansible-lint"
15
+ builds = ["macos", "linux"]
16
+
17
+ [[tool_file]]
18
+ target = ".gspot/config/ansible-lint.yml"
19
+ tool = "ansible-lint"
20
+ per_scope = true
21
+
22
+ [[check]]
23
+ name = "ansible-lint"
24
+ example = "A task using `ansible.builtin.command: systemctl restart nginx` reports `command-instead-of-module`. Use `ansible.builtin.systemd` with `name: nginx` and `state: restarted` to express the intended service operation."
25
+ level = "recommended"
26
+ stage = "commit"
27
+ runs = "scope"
28
+ tool = "ansible-lint"
29
+ command = ["ansible-lint", "-c", "{tool_file:ansible-lint}", "--offline", "--nocolor", "-f", "pep8", "--profile", "{level:moderate:production}"]
30
+ files = { extensions = [".yml", ".yaml", ".cfg", ".j2"] }
31
+ summary = "Runs the moderate ansible-lint profile at recommended and production at all, beside each ansible.cfg or at the selected scope root."
32
+ why = "A playbook runs as root on production hosts, and a task that is not idempotent changes them on every run."
33
+ help = "Change the task the way the rule says. Turn one rule off with gspot ignore ansible/ansible-lint --rule `<id>` --reason \"`<why>`\"."
@@ -0,0 +1,127 @@
1
+ prefix_collisions = { prefixes = ["docker"] }
2
+
3
+ allowed_words = ["Docker", "Trivy"]
4
+ [configuration]
5
+ title = "Docker"
6
+ description = "Dockerfiles and Compose files: hadolint, a Compose file that parses, an ignore file beside every Dockerfile, and Trivy over the configuration."
7
+
8
+ [detect]
9
+ paths = ["**/Dockerfile", "**/Dockerfile.*", "**/*.dockerfile", "**/docker-compose*.yml", "**/docker-compose*.yaml", "**/compose*.yml", "**/compose*.yaml", "!**/*.dockerignore"]
10
+
11
+ [files]
12
+ paths = ["**/Dockerfile", "**/Dockerfile.*", "**/*.dockerfile", "**/docker-compose*.yml", "**/docker-compose*.yaml", "**/compose*.yml", "**/compose*.yaml", "**/.dockerignore", "**/*.dockerignore"]
13
+
14
+ [[tool]]
15
+ name = "hadolint"
16
+ version = "2.14.0"
17
+ mise = "hadolint"
18
+ brew = "hadolint"
19
+ github = { name = "hadolint/hadolint", version = "v2.14.0" }
20
+ scoop = "hadolint"
21
+ [[tool.replace]]
22
+ file = ".hadolint.yaml"
23
+ shared = false
24
+
25
+ [[tool.replace]]
26
+ file = ".hadolint.yml"
27
+ shared = false
28
+
29
+ [[tool]]
30
+ name = "trivy"
31
+ version = "0.69.3"
32
+ mise = "trivy"
33
+ brew = "trivy"
34
+ github = { name = "aquasecurity/trivy", version = "v0.69.3" }
35
+ [[tool.replace]]
36
+ file = "trivy.yaml"
37
+ shared = false
38
+
39
+ [[tool.replace]]
40
+ file = ".trivyignore"
41
+ shared = false
42
+
43
+ [[tool]]
44
+ name = "docker"
45
+ install_hint = "install Docker Desktop or the docker engine"
46
+ system = true
47
+
48
+ [[tool_file]]
49
+ target = ".gspot/config/hadolint.yml"
50
+ tool = "hadolint"
51
+ per_scope = true
52
+ rule_keys = ["ignored"]
53
+
54
+ [[tool_file]]
55
+ target = ".gspot/config/trivy.yml"
56
+ tool = "trivy"
57
+ per_scope = true
58
+
59
+ [[tool_file]]
60
+ source = "trivyignore"
61
+ target = ".gspot/config/trivyignore"
62
+ tool = "trivy"
63
+ per_scope = true
64
+
65
+ [[check]]
66
+ name = "hadolint"
67
+ example = "The instruction `FROM node:latest` reports DL3007 because the tag changes over time. Select a reviewed, explicit version tag or digest and rerun."
68
+ level = "recommended"
69
+ stage = "commit"
70
+ command = ["hadolint", "--no-color", "--config", "{tool_file:hadolint}", "{files}"]
71
+ files = { paths = ["**/Dockerfile", "**/Dockerfile.*", "**/*.dockerfile", "!**/*.dockerignore"] }
72
+ output = { format = "regex", pattern = "^(?<file>.+?):(?<line>\\d+) (?<rule>[A-Z]+\\d+) [a-z]+: (?<message>.*)$" }
73
+ summary = "Lints every Dockerfile, and runs ShellCheck over its RUN lines."
74
+ why = "An unpinned base image or a cache left in a layer makes a build nobody can repeat and an image larger than it needs to be."
75
+ help = "Change the instruction the way the rule says. Turn one rule off with gspot ignore docker/hadolint --rule `<id>` --reason \"`<why>`\"."
76
+
77
+ [[check]]
78
+ name = "compose"
79
+ example = "A Compose service with an unknown key such as `bogus` fails configuration validation. Remove the key or replace it with the supported setting for the intended behavior."
80
+ level = "recommended"
81
+ stage = "push"
82
+ needs = ["docker"]
83
+ tool = "docker"
84
+ runs = "scope"
85
+ command = ["docker", "compose", "-f", "{file}", "config", "--quiet", "--no-env-resolution"]
86
+ files = { paths = ["**/docker-compose*.yml", "**/docker-compose*.yaml", "**/compose*.yml", "**/compose*.yaml"] }
87
+ output = { format = "lines" }
88
+ summary = "Asks Docker Compose to parse every Compose file, which needs no running daemon."
89
+ why = "A Compose file that is valid YAML can still name a key Compose refuses, and the first person to learn that is deploying."
90
+ help = "Read the message Compose prints; it names the key and the service."
91
+
92
+ [[check]]
93
+ name = "dockerignore"
94
+ example = "A Dockerfile without a neighboring `.dockerignore` reports the missing context exclusions. Add `.dockerignore` with the required entries named in the finding, such as `.git` and dependency directories."
95
+ level = "recommended"
96
+ stage = "commit"
97
+ runs = "once"
98
+ summary = "Checks that an ignore file sits beside every Dockerfile and keeps the git folder, installed dependencies, and environment files out of the build."
99
+ why = "Without it the whole folder goes to the daemon, and copying the folder puts the history and the secrets into the image."
100
+ help = "Add a .dockerignore beside the Dockerfile with the entries the message names."
101
+
102
+ [[check]]
103
+ name = "trivy-config"
104
+ example = "A Dockerfile whose final application stage runs as root can report a container-security finding. Create an unprivileged user, grant it only required file access, and select it with `USER` before running the application."
105
+ level = "recommended"
106
+ stage = "push"
107
+ runs = "scope"
108
+ run_in_copy = true
109
+ command = ["trivy", "config", "--misconfig-scanners", "dockerfile", "--quiet", "--skip-check-update", "--config", "{tool_file:trivy}", "--ignorefile", "{tool_file:trivyignore}", "--exit-code", "10", "--format", "sarif", "{root}"]
110
+ tool = "trivy"
111
+ exit_codes = [10]
112
+ output = { format = "sarif" }
113
+ summary = "Scans Dockerfiles for HIGH and CRITICAL misconfigurations, including a root user and exposed secrets."
114
+ why = "A container that runs as root turns any bug in the service into control of the container."
115
+ help = "Change the file the way the message says. Accept one advisory with gspot ignore docker/trivy-config --rule `<id>` --reason \"...\"."
116
+
117
+ [[check]]
118
+ name = "trivy-image"
119
+ example = "A configured image containing a package with a known advisory reports that advisory. Move to a reviewed image version containing the package fix, then rerun the image scan. Results depend on the advisory database and available image."
120
+ level = "recommended"
121
+ stage = "manual"
122
+ runs = "scope"
123
+ needs = ["docker"]
124
+ tool = "trivy"
125
+ summary = "Scans every image a Compose file names for known vulnerabilities in its packages."
126
+ why = "A base image carries hundreds of packages, and their advisories arrive after the image was chosen."
127
+ help = "Move to a newer tag of the image. Accept one advisory with gspot ignore docker/trivy-image --rule `<id>` --reason \"...\"."
@@ -0,0 +1,135 @@
1
+ # Docker
2
+
3
+ ## Image inputs
4
+
5
+ Select a base image that supports the application's runtime, target architecture, native libraries,
6
+ and certificate requirements. Tags can change, so pin a reviewed digest when the build requires
7
+ immutable input, and keep an update process for security fixes: a digest never patches itself.
8
+
9
+ Use a supported runtime release. For native modules, keep the build and runtime operating system,
10
+ libc, and architecture compatible. Alpine, Debian, and distroless images have different runtime
11
+ and operational requirements; no single variant fits every application.
12
+
13
+ ## Build and runtime stages
14
+
15
+ Build-only compilers, test dependencies, and package caches stay out of the final image. It keeps
16
+ interpreted source, declared debug artifacts, and any other file the runtime reads.
17
+ Private source maps stay out of publicly served assets. When compilation or dependency preparation
18
+ needs tools the runtime does not, a multi-stage build copies only the runtime inputs into the final
19
+ stage. Host `node_modules` never enter a Linux image.
20
+
21
+ This npm example assumes a `build` task that writes `dist/main.js`, a committed lockfile, and
22
+ Node.js 24; a release build that requires immutable input pins the reviewed digest.
23
+
24
+ ```dockerfile
25
+ FROM node:24-bookworm-slim AS build
26
+ WORKDIR /app
27
+ COPY package.json package-lock.json ./
28
+ RUN npm ci
29
+ COPY tsconfig.json ./
30
+ COPY src ./src
31
+ RUN npm run build
32
+
33
+ FROM node:24-bookworm-slim AS dependencies
34
+ WORKDIR /app
35
+ COPY package.json package-lock.json ./
36
+ RUN npm ci --omit=dev
37
+
38
+ FROM node:24-bookworm-slim AS runtime
39
+ ENV NODE_ENV=production
40
+ WORKDIR /app
41
+ COPY --chown=node:node package.json ./
42
+ COPY --from=dependencies --chown=node:node /app/node_modules ./node_modules
43
+ COPY --from=build --chown=node:node /app/dist ./dist
44
+ USER node
45
+ CMD ["node", "dist/main.js"]
46
+ ```
47
+
48
+ Use the package manager the project declares; for Bun, its pinned version with
49
+ `bun install --frozen-lockfile`, adding `--production` for runtime dependencies. The final image
50
+ holds a package-manager binary only when the runtime uses it. A statically linked `server` built
51
+ for the target platform needs no package manager or shell, only the certificates, time-zone data,
52
+ or other runtime files it reads.
53
+
54
+ ```dockerfile
55
+ FROM scratch
56
+ COPY server /server
57
+ USER 65532:65532
58
+ ENTRYPOINT ["/server"]
59
+ ```
60
+
61
+ ## Package installation and downloads
62
+
63
+ hadolint reports unpinned images and packages, apt cleanup, `ADD` for local files, and shell-form
64
+ commands. Review these additional requirements:
65
+
66
+ - Install application dependencies from the declared lockfile without updating it during the build.
67
+ - Refresh repository metadata and install packages in the same `RUN`, so cached metadata cannot
68
+ select an unintended version.
69
+ - Refresh base images for operating-system fixes. A targeted package upgrade needs an explicit
70
+ dependency change, never an unbounded distribution upgrade in every build.
71
+ - Verify a downloaded executable's digest or signature before installing it.
72
+ - Remove installation-only files and caches in the layer that creates them.
73
+
74
+ ### Dockerfile organization
75
+
76
+ <!-- level: all -->
77
+
78
+ Put stable build inputs before frequently changing application source when that improves cache
79
+ reuse. Put substantial shell behavior in an existing script. Keep the final image's user,
80
+ working directory, environment, and startup command in documented locations. Do not add a wrapper script
81
+ for a command the container can run directly.
82
+
83
+ ## Build context and ignore files
84
+
85
+ The context holds only the files the build needs, and `.dockerignore` changes with every
86
+ Dockerfile that uses it. A deny-by-default context suits a small, stable input set.
87
+
88
+ - Never exclude `dist`, generated dependency exports, or build scripts by name alone: a build that
89
+ copies verified prebuilt output needs it. Every `COPY` source survives the ignore rules.
90
+
91
+ ## Build secrets
92
+
93
+ Use BuildKit secret mounts for private registry authentication and other build credentials.
94
+ Read the secret only inside the `RUN` instruction that requires it. Copying it into a layer keeps
95
+ it in image history even if a later layer removes it.
96
+
97
+ ## Shell and process behavior
98
+
99
+ Use the shell syntax actually selected for `RUN`; the default shell does not run Bash. A required
100
+ entrypoint script validates configuration and uses `exec` for its final handoff. A documented
101
+ process supervisor forwards signals and reaps its children, and no init wrapper hides missing
102
+ shutdown behavior in the application.
103
+
104
+ The application's shutdown limit fits the orchestrator's grace period.
105
+
106
+ ## Runtime
107
+
108
+ - Durable state lives in explicit volumes or external storage. Environment-specific values arrive
109
+ at runtime. A production Node.js
110
+ service sets `NODE_ENV=production` where the runtime expects it.
111
+ - Expose only the required ports, and bind private local services to localhost. Never add Linux
112
+ capabilities, public debug ports, or disabled TLS verification to make a build or service work.
113
+ - Resource limits fit the deployment and leave room for native memory, buffers, and runtime
114
+ overhead beyond a language heap limit. Ask the user for memory measurements before changing a limit
115
+ when the task provides none.
116
+
117
+ ## Compose
118
+
119
+ Compose declares the wiring: ports, volumes, secrets, resource limits, health checks, restart
120
+ policy, and dependencies. Behavior stays in the application. `depends_on` orders startup, with the
121
+ health condition when a service needs another one ready. A production container never bind-mounts
122
+ development source or host dependencies.
123
+
124
+ ## Verification
125
+
126
+ Do not build or run images unless the user asks or the repository's checks require it. Give the user a list of items to check: the image's user, command, runtime files, permissions, exposed ports, startup,
127
+ readiness, shutdown, and resource limits. Image documentation names the current build arguments,
128
+ supported platforms, runtime environment, and artifact paths.
129
+
130
+ ## References
131
+
132
+ | Topic | Primary source |
133
+ | ------------- | ------------------------------------------------------------------------------------- |
134
+ | Builds | [Docker build best practices](https://docs.docker.com/build/building/best-practices/) |
135
+ | Build secrets | [Secret mounts](https://docs.docker.com/reference/dockerfile/#run---mounttypesecret) |
@@ -0,0 +1,2 @@
1
+ # This file stops Trivy from reading the repository .trivyignore.
2
+ # Accept a finding with gspot ignore docker/trivy-config and a reason.
@@ -0,0 +1,76 @@
1
+ allowed_words = ["GitHub"]
2
+ [configuration]
3
+ title = "GitHub Actions"
4
+ description = "GitHub Actions workflows: actionlint for syntax and expressions, pinact for the commits actions are pinned to, and zizmor for security audits."
5
+
6
+ [files]
7
+ paths = ["**/.github/workflows/*.{yml,yaml}", "**/action.{yml,yaml}"]
8
+
9
+ [[tool]]
10
+ name = "actionlint"
11
+ version = "1.7.12"
12
+ mise = "actionlint"
13
+ brew = "actionlint"
14
+ github = { name = "rhysd/actionlint", version = "v1.7.12" }
15
+
16
+ [[tool]]
17
+ name = "shellcheck"
18
+
19
+ [[tool]]
20
+ name = "pinact"
21
+ crash_pattern = '(?:ERROR|FATAL).*?(?:\b(?:401|403|408|429|5[0-9]{2})\b|dial tcp|TLS handshake|x509:|context deadline exceeded|connection reset|no such host)'
22
+ version = "5.0.0"
23
+ mise = "pinact"
24
+ brew = "pinact"
25
+ github = { name = "suzuki-shunsuke/pinact", version = "v5.0.0" }
26
+
27
+ [[tool]]
28
+ name = "zizmor"
29
+ version = "1.30.1"
30
+ mise = "zizmor"
31
+ pypi = "zizmor"
32
+ brew = "zizmor"
33
+ cargo = "zizmor"
34
+
35
+ [[tool_file]]
36
+ source = "zizmor.yml"
37
+ target = ".gspot/config/zizmor.yml"
38
+ tool = "zizmor"
39
+
40
+ [[check]]
41
+ name = "actionlint"
42
+ example = "A workflow using an unknown expression context such as `${{ unknown.value }}` reports an actionlint error. Replace it with a supported context and property for that workflow location."
43
+ level = "recommended"
44
+ stage = "commit"
45
+ tool = ["actionlint", "shellcheck"]
46
+ command = ["actionlint", "-no-color", "{files}"]
47
+ files = { paths = ["**/.github/workflows/*.{yml,yaml}"] }
48
+ exit_codes = [1]
49
+ output = { format = "regex", pattern = "^(?<file>[^:]+):(?<line>\\d+):(?<column>\\d+): (?<message>.*?) \\[(?<rule>[a-z-]+)\\]$" }
50
+ summary = "Checks every GitHub Actions workflow with actionlint: syntax, expressions, action inputs, and shell steps."
51
+ why = "A workflow error surfaces on the first push after the change, in CI, where it costs a round trip."
52
+ help = "Fix the line actionlint names."
53
+
54
+ [[check]]
55
+ name = "pinact"
56
+ example = "An action reference such as `actions/checkout@v4` without a full commit pin reports an unpinned action. Resolve a reviewed release to its full commit SHA and record the matching version comment beside the pin."
57
+ level = "recommended"
58
+ stage = "push"
59
+ needs = ["network"]
60
+ command = ["pinact", "run", "--verify-comment", "--check", "{files}"]
61
+ output = { format = "lines" }
62
+ summary = "Checks that each GitHub action has a full commit pin that exists and a matching version comment."
63
+ why = "A tag can move between runs, a nonexistent commit prevents the workflow from starting, and a wrong version comment hides the intended release."
64
+ help = "Replace the invalid action pin with a verified commit and its matching version comment."
65
+
66
+ [[check]]
67
+ name = "zizmor"
68
+ example = "A workflow interpolating pull-request text directly into a shell command can report a template-injection audit. Pass the text through a step environment variable and quote its expansion instead of interpolating it into shell code."
69
+ level = "recommended"
70
+ stage = "commit"
71
+ command = ["zizmor", "--offline", "--no-progress", "--format", "github", "--config", "{tool_file:zizmor}", "{files}"]
72
+ exit_codes = [11, 12, 13, 14]
73
+ output = { format = "regex", pattern = "^::(?:warning|error|notice) file=(?<file>[^,]+),line=(?<line>\\d+),title=(?<rule>[^:]+)::(?<message>.*)$" }
74
+ summary = "Audits GitHub Actions workflows and composite actions with zizmor for template injection, excessive permissions, and dangerous triggers."
75
+ why = "A workflow runs with the repository's credentials; an injectable expression can hand them to a stranger."
76
+ help = "Apply the fix zizmor names, or turn one audit off with gspot ignore github-actions/zizmor --rule `<audit>` --reason \"`<why>`\"."
@@ -0,0 +1,43 @@
1
+ # GitHub Actions
2
+
3
+ ## Workflow shape
4
+
5
+ - Every job has `timeout-minutes`. Every workflow that can run twice for one ref has a
6
+ `concurrency` group with `cancel-in-progress` for pull requests.
7
+ - Pin a runner image such as `ubuntu-24.04` for each job. Do not use `ubuntu-latest`.
8
+ - No `continue-on-error` without a reason comment. Required failures must fail the job.
9
+ - Upload diagnostics after failed checks with an explicit status condition. Deployments and
10
+ release publication require successful prerequisites.
11
+
12
+ ## Repository actions and secrets
13
+
14
+ - Reference first-party actions from the repository by path.
15
+ - Read secrets through `${{ secrets.NAME }}` into an `env` for the one step that needs them.
16
+ Never write them to files that outlive the step.
17
+
18
+ ## Scripts
19
+
20
+ - Declare the shell required by the script. The explicit Bash shell in GitHub Actions enables `-e` and
21
+ `pipefail`, but does not enable `-u`. Enable required options and validate inputs explicitly.
22
+ - Key caches on the lockfile hash and the runtime version. Restore keys never match a
23
+ different lockfile.
24
+
25
+ ## Outputs
26
+
27
+ - Artifacts and summaries name the job and the ref. Set retention explicitly.
28
+ - Give deploy credentials only to the workflow that deploys. Run it on a protected
29
+ environment with a reviewer.
30
+
31
+ See [the GitHub shell configuration](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idstepsshell)
32
+ for the command each runner uses.
33
+
34
+ ## Workflow organization
35
+
36
+ <!-- level: all -->
37
+
38
+ - Give each workflow one purpose, such as checks, release publication, or deployment.
39
+ - Call existing task-runner commands instead of copying their implementation into a workflow.
40
+ - Keep substantial logic in scripts that run locally through the task runner and in CI without
41
+ a prompt. Run the checks their language requires.
42
+ - A verification job changes source only when its task requires formatting or code generation.
43
+ - Write logs and reports to predictable artifact paths.
@@ -0,0 +1,3 @@
1
+ rules:
2
+ unpinned-uses:
3
+ disable: true
@@ -0,0 +1,51 @@
1
+ allowed_words = ["nginx"]
2
+ [configuration]
3
+ title = "nginx"
4
+ description = "nginx configuration: gixy for the security mistakes, and the server's own parser in a container for the rest."
5
+
6
+ [files]
7
+ paths = ["**/nginx.conf", "**/nginx/**/*.conf"]
8
+
9
+ [[tool]]
10
+ name = "gixy"
11
+ version = "0.2.53"
12
+ pypi = "gixy-ng"
13
+ mise = "pipx:gixy-ng"
14
+ builds = ["macos", "linux"]
15
+ version_pattern = "(\\d+\\.\\d+\\.\\d+)"
16
+
17
+ [[tool]]
18
+ name = "docker"
19
+
20
+ [[tool]]
21
+ name = "openssl"
22
+ system = true
23
+
24
+ [[tool_file]]
25
+ target = ".gspot/config/gixy.cfg"
26
+ tool = "gixy"
27
+ rule_keys = ["skips"]
28
+
29
+ [[check]]
30
+ name = "gixy"
31
+ example = "A location matching `/proxy/(.*)` with `proxy_pass http://$1` lets the request choose the destination and reports `ssrf`. Remove the request-controlled proxy destination and use the intended fixed upstream, or remove the proxy operation when the endpoint only needs to return a response."
32
+ level = "recommended"
33
+ stage = "commit"
34
+ command = ["gixy", "--config", "{tool_file:gixy}", "--format", "json", "{file}"]
35
+ output = { format = "json", fields = { file = "path", line = "line", rule = "plugin", message = "summary" } }
36
+ summary = "Reads every nginx file for the mistakes that open a server: request forgery, path traversal through alias, header injection, a disclosed version."
37
+ why = "These mistakes parse, serve traffic, and look like any other location block until somebody uses them."
38
+ help = "Change the block the way the rule's page says. Turn one rule off with gspot ignore nginx/gixy --rule `<name>` --reason \"`<why>`\"."
39
+
40
+ [[check]]
41
+ name = "config-test"
42
+ example = "The line `invalid_directive on;` fails the native configuration test with `nginx -t`. Remove the unknown directive or replace it with the intended supported directive, such as `listen 443 ssl;` in the configured TLS server. Rerun with the configured deployment image available."
43
+ level = "recommended"
44
+ stage = "push"
45
+ runs = "scope"
46
+ needs = ["docker"]
47
+ tool = "docker"
48
+ files = { filenames = ["nginx.conf"] }
49
+ summary = "Validates each main configuration inside the nginx image and records the files nginx parsed. It mounts a throwaway certificate and resolves upstream names."
50
+ why = "Only nginx knows whether nginx accepts the file, and the other place to learn it is the restart in production."
51
+ help = "Read the line nginx names. Set tools.nginx.image to the image the deployment runs."
@@ -0,0 +1,38 @@
1
+ # `nginx`
2
+
3
+ ## Edge responsibilities
4
+
5
+ - nginx must not encode product authorization or feature behavior.
6
+
7
+ ## Configuration
8
+
9
+ - One `server` block per hostname. Redirect the plain-HTTP server to HTTPS and every alias to
10
+ the canonical host.
11
+ - TLS: `ssl_protocols TLSv1.2 TLSv1.3`, a modern cipher list from the platform's generator,
12
+ and an explicit session-ticket policy. Enable Online Certificate Status Protocol (OCSP) stapling only when the certificate issuer
13
+ supports it and you can verify its response.
14
+ - Put the application's security-header directives in one shared include. Use `always` where a
15
+ header must also appear on error responses.
16
+ - `client_max_body_size` per location, matching the application route limits, with a small
17
+ default.
18
+ - Set explicit `proxy_read_timeout`, `proxy_connect_timeout`, and `send_timeout` values that align
19
+ with the application's shutdown grace period.
20
+ - Name rate-limit zones for what they protect, such as `zone=login_attempts`, and size them with a
21
+ comment stating the budget.
22
+ - Set proxy headers according to the upstream trust contract. Overwrite untrusted client
23
+ values where required. The application trusts forwarding metadata only from approved proxies.
24
+ - Use immutable caching only for content-addressed assets. Give mutable resources a cache
25
+ lifetime and revalidation policy consistent with their update process.
26
+ - Keep configuration in the repository, lint it before commit, and reload it through the
27
+ deployment flow, never edited on the host.
28
+
29
+ ## Images
30
+
31
+ - Pin the nginx image to a full version tag of the variant the project uses, never `nginx:alpine`
32
+ or `latest`.
33
+ - The image copies the configuration and nothing else. No shell in the entrypoint.
34
+
35
+ See [nginx TLS configuration](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling)
36
+ for stapling requirements. Certificate authorities can
37
+ [retire OCSP](https://letsencrypt.org/2024/12/05/ending-ocsp); verify issuer support
38
+ before enabling stapling.
@@ -0,0 +1,50 @@
1
+ [configuration]
2
+ title = "OpenAPI"
3
+ description = "A committed OpenAPI document: Spectral lints it, and the command that writes it must not change it."
4
+
5
+ [detect]
6
+ filenames = ["openapi.yaml", "openapi.yml", "openapi.json", "swagger.json", "swagger.yaml"]
7
+
8
+ [[tool]]
9
+ name = "spectral"
10
+ version = "6.15.0"
11
+ npm = "@stoplight/spectral-cli"
12
+ mise = "npm:@stoplight/spectral-cli"
13
+
14
+ # Spectral's Ajv plugins must use the same ajv package as Spectral, so the tool project installs one shared ajv.
15
+
16
+ [[tool]]
17
+ name = "ajv"
18
+
19
+ [[tool_file]]
20
+ target = ".gspot/config/spectral.yaml"
21
+ tool = "spectral"
22
+
23
+ [[check]]
24
+ name = "spectral"
25
+ example = "An OpenAPI document whose operation references an undefined schema reports a validation finding. Define the schema or correct the reference in the source that produces the document, then regenerate it."
26
+ level = "recommended"
27
+ stage = "commit"
28
+ runs = "scope"
29
+ tool = "spectral"
30
+ cwd = "root"
31
+ command = ["spectral", "lint", "--ruleset", "{tool_file:spectral}", "--format", "text", "{setting:openapi.document}"]
32
+ when = { setting = "openapi.document" }
33
+ output = { format = "regex", pattern = '^(?<file>.+):(?<line>\d+):(?<column>\d+) (?:error|warning) (?<rule>\S+) "(?<message>.*)"$' }
34
+ files = { extensions = [".yaml", ".yml", ".json"] }
35
+ summary = "Lints the OpenAPI document named in openapi.document with the Spectral OpenAPI rules."
36
+ why = "Clients are written from this document, so an operation with no id or an undefined response becomes somebody's broken client."
37
+ help = "Change the document the way the rule says, or the code that writes it."
38
+
39
+ [[check]]
40
+ name = "stale-document"
41
+ command = ["{setting:openapi.generate_command}"]
42
+ when = { setting = "openapi.generate_command" }
43
+ generated_paths = [{ path = "{setting:openapi.document}" }]
44
+ example = "A committed OpenAPI document that differs from the configured generator output reports drift. Run the declared generator, inspect the changed document, and retain the corrected output before rerunning."
45
+ level = "recommended"
46
+ stage = "push"
47
+ runs = "scope"
48
+ summary = "Runs the command in openapi.generate_command and fails when it changes the document."
49
+ why = "A committed document that lags the routes describes a service that does not exist."
50
+ help = "Run the command and commit the document it writes."