@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.
- package/README.md +18 -2
- package/configurations/database/postgres/manifest.toml +114 -0
- package/configurations/database/postgres/rules/POSTGRES.md +78 -0
- package/configurations/framework/astro/eslint-presets.json +138 -0
- package/configurations/framework/astro/manifest.toml +95 -0
- package/configurations/framework/astro/rules/ASTRO.md +39 -0
- package/configurations/framework/expo/manifest.toml +43 -0
- package/configurations/framework/express/manifest.toml +17 -0
- package/configurations/framework/express/rules/EXPRESS.md +73 -0
- package/configurations/framework/express/semgrep/express.yml +71 -0
- package/configurations/framework/fastapi/manifest.toml +11 -0
- package/configurations/framework/fastapi/rules/FASTAPI.md +100 -0
- package/configurations/framework/fastapi/semgrep/fastapi.yml +44 -0
- package/configurations/framework/nestjs/eslint-presets.json +62 -0
- package/configurations/framework/nestjs/manifest.toml +52 -0
- package/configurations/framework/nestjs/rules/NESTJS.md +44 -0
- package/configurations/framework/nextjs/eslint-presets.json +73 -0
- package/configurations/framework/nextjs/manifest.toml +119 -0
- package/configurations/framework/nextjs/rules/NEXTJS.md +43 -0
- package/configurations/framework/react/eslint-presets.json +77 -0
- package/configurations/framework/react/manifest.toml +93 -0
- package/configurations/framework/react/rules/REACT.md +108 -0
- package/configurations/framework/react-dom/eslint-presets.json +110 -0
- package/configurations/framework/react-dom/manifest.toml +24 -0
- package/configurations/framework/react-native/manifest.toml +46 -0
- package/configurations/framework/react-native/rules/REACT-NATIVE.md +51 -0
- package/configurations/framework/site/manifest.toml +170 -0
- package/configurations/framework/site/rules/SITE.md +20 -0
- package/configurations/framework/svelte/eslint-presets.json +67 -0
- package/configurations/framework/svelte/manifest.toml +83 -0
- package/configurations/framework/svelte/rules/SVELTE.md +48 -0
- package/configurations/framework/vue/eslint-presets.json +180 -0
- package/configurations/framework/vue/manifest.toml +83 -0
- package/configurations/framework/vue/rules/VUE.md +41 -0
- package/configurations/general/commits/manifest.toml +111 -0
- package/configurations/general/commits/rules/GIT.md +31 -0
- package/configurations/general/coverage/manifest.toml +3 -0
- package/configurations/general/dependencies/manifest.toml +159 -0
- package/configurations/general/dependencies/rules/DEPENDENCIES.md +30 -0
- package/configurations/general/dependencies/syncpack.json +15 -0
- package/configurations/general/docs/manifest.toml +79 -0
- package/configurations/general/docs/rules/DOCS.md +86 -0
- package/configurations/general/duplication/manifest.toml +32 -0
- package/configurations/general/engineering/manifest.toml +8 -0
- package/configurations/general/engineering/rules/agent/PLANNING.md +42 -0
- package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +16 -0
- package/configurations/general/engineering/rules/agent/TALKING.md +3 -0
- package/configurations/general/engineering/rules/agent/TASKS.md +19 -0
- package/configurations/general/engineering/rules/agent/WORKING.md +83 -0
- package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +32 -0
- package/configurations/general/engineering/rules/code/CLI.md +37 -0
- package/configurations/general/engineering/rules/code/COMMENTS.md +38 -0
- package/configurations/general/engineering/rules/code/CONFIGURATION.md +25 -0
- package/configurations/general/engineering/rules/code/ERRORS.md +49 -0
- package/configurations/general/engineering/rules/code/HTTP.md +46 -0
- package/configurations/general/engineering/rules/code/LOGGING.md +55 -0
- package/configurations/general/engineering/rules/code/TESTING.md +58 -0
- package/configurations/general/files/manifest.toml +141 -0
- package/configurations/general/files/rules/YAML.md +19 -0
- package/configurations/general/format/manifest.toml +152 -0
- package/configurations/general/gspot/manifest.toml +65 -0
- package/configurations/general/gspot/rules/GENERATED.md +10 -0
- package/configurations/general/licenses/manifest.toml +33 -0
- package/configurations/general/naming/defaults.json +423 -0
- package/configurations/general/naming/manifest.toml +35 -0
- package/configurations/general/naming/rules/NAMING.md +179 -0
- package/configurations/general/prose/manifest.toml +86 -0
- package/configurations/general/prose/rules/WRITING.md +31 -0
- package/configurations/general/prose/vale/styles/LICENSES.md +55 -0
- package/configurations/general/prose/vale/styles/gspot/ampm.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/conscious-language.yml +16 -0
- package/configurations/general/prose/vale/styles/gspot/contractions.yml +43 -0
- package/configurations/general/prose/vale/styles/gspot/currency.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/dashes.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/date-ordinals.yml +41 -0
- package/configurations/general/prose/vale/styles/gspot/dates.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/file-paths.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/future.yml +17 -0
- package/configurations/general/prose/vale/styles/gspot/hedging.yml +22 -0
- package/configurations/general/prose/vale/styles/gspot/history.yml +16 -0
- package/configurations/general/prose/vale/styles/gspot/idioms.yml +21 -0
- package/configurations/general/prose/vale/styles/gspot/interface-verbs.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/jargon.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/leading-conjunctions.yml +12 -0
- package/configurations/general/prose/vale/styles/gspot/link-text.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/locations.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/marketing.yml +26 -0
- package/configurations/general/prose/vale/styles/gspot/merge-conflict-markers.yml +13 -0
- package/configurations/general/prose/vale/styles/gspot/militaristic-language.yml +22 -0
- package/configurations/general/prose/vale/styles/gspot/modals.yml +9 -0
- package/configurations/general/prose/vale/styles/gspot/numbered-units.yml +18 -0
- package/configurations/general/prose/vale/styles/gspot/obvious-fields.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/ordinal-adverbs.yml +15 -0
- package/configurations/general/prose/vale/styles/gspot/percentage-numbers.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/placeholders.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/product-centric-writing.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/punctuated-headings.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/self-reference.yml +15 -0
- package/configurations/general/prose/vale/styles/gspot/session-identifiers.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/since.yml +6 -0
- package/configurations/general/prose/vale/styles/gspot/straight-quotes.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/suspended-hyphens.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/symbols.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/time-ranges.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/url-prepositions.yml +10 -0
- package/configurations/general/prose/vale/styles/gspot/version-range.yml +8 -0
- package/configurations/general/repository/manifest.toml +24 -0
- package/configurations/general/secrets/gitleaks.toml +2 -0
- package/configurations/general/secrets/manifest.toml +125 -0
- package/configurations/general/secrets/rules/SECRETS.md +13 -0
- package/configurations/general/security/manifest.toml +77 -0
- package/configurations/general/security/rules/SECURITY.md +55 -0
- package/configurations/general/spelling/manifest.toml +46 -0
- package/configurations/general/structure/manifest.toml +70 -0
- package/configurations/infrastructure/ansible/manifest.toml +33 -0
- package/configurations/infrastructure/docker/manifest.toml +127 -0
- package/configurations/infrastructure/docker/rules/DOCKER.md +135 -0
- package/configurations/infrastructure/docker/trivyignore +2 -0
- package/configurations/infrastructure/github-actions/manifest.toml +76 -0
- package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +43 -0
- package/configurations/infrastructure/github-actions/zizmor.yml +3 -0
- package/configurations/infrastructure/nginx/manifest.toml +51 -0
- package/configurations/infrastructure/nginx/rules/NGINX.md +38 -0
- package/configurations/infrastructure/openapi/manifest.toml +50 -0
- package/configurations/infrastructure/openapi/rules/OPENAPI.md +39 -0
- package/configurations/infrastructure/xcode/manifest.toml +131 -0
- package/configurations/infrastructure/xcode/rules/XCODE.md +34 -0
- package/configurations/language/bash/manifest.toml +261 -0
- package/configurations/language/bash/rules/BASH.md +106 -0
- package/configurations/language/bash/rules/LANGUAGE.md +213 -0
- package/configurations/language/bash/rules/NAMING.md +62 -0
- package/configurations/language/bash/rules/OPERATIONS.md +77 -0
- package/configurations/language/bash/rules/SAFETY.md +222 -0
- package/configurations/language/bash/semgrep/bash.yml +30 -0
- package/configurations/language/css/manifest.toml +98 -0
- package/configurations/language/css/rules/CSS.md +28 -0
- package/configurations/language/css/rules/NAMING.md +17 -0
- package/configurations/language/css/rules/TAILWIND.md +26 -0
- package/configurations/language/css/stylelint-rule-names.json +89 -0
- package/configurations/language/html/manifest.toml +59 -0
- package/configurations/language/html/rules/HTML.md +27 -0
- package/configurations/language/html/rules/NAMING.md +16 -0
- package/configurations/language/javascript/eslint-presets.json +997 -0
- package/configurations/language/javascript/eslint-rule-names.json +299 -0
- package/configurations/language/javascript/eslint-runtime-names.json +61 -0
- package/configurations/language/javascript/manifest.toml +393 -0
- package/configurations/language/javascript/rules/BUN.md +13 -0
- package/configurations/language/javascript/rules/COMPONENTS.md +18 -0
- package/configurations/language/javascript/rules/DENO.md +15 -0
- package/configurations/language/javascript/rules/JAVASCRIPT.md +60 -0
- package/configurations/language/javascript/rules/NAMING.md +31 -0
- package/configurations/language/javascript/rules/NODE.md +21 -0
- package/configurations/language/javascript/rules/PLAYWRIGHT.md +34 -0
- package/configurations/language/javascript/semgrep/javascript.yml +84 -0
- package/configurations/language/markdown/manifest.toml +85 -0
- package/configurations/language/markdown/markdownlint-cli2.mjs +19 -0
- package/configurations/language/python/manifest.toml +621 -0
- package/configurations/language/python/rules/DESIGN.md +136 -0
- package/configurations/language/python/rules/FLOW.md +124 -0
- package/configurations/language/python/rules/NAMING.md +53 -0
- package/configurations/language/python/rules/PACKAGING.md +49 -0
- package/configurations/language/python/rules/PYTHON.md +136 -0
- package/configurations/language/python/rules/TYPING.md +132 -0
- package/configurations/language/sql/manifest.toml +68 -0
- package/configurations/language/sql/rules/NAMING.md +90 -0
- package/configurations/language/sql/rules/SQL.md +9 -0
- package/configurations/language/swift/manifest.toml +164 -0
- package/configurations/language/swift/rules/NAMING.md +88 -0
- package/configurations/language/swift/rules/SWIFT.md +134 -0
- package/configurations/language/swift/rules/SWIFTUI.md +61 -0
- package/configurations/language/swift/rules/UIKIT.md +99 -0
- package/configurations/language/swift/semgrep/swift.yml +126 -0
- package/configurations/language/typescript/eslint-presets.json +215 -0
- package/configurations/language/typescript/manifest.toml +72 -0
- package/configurations/language/typescript/rules/NAMING.md +73 -0
- package/configurations/language/typescript/rules/TYPESCRIPT.md +79 -0
- package/configurations/library/drizzle/manifest.toml +68 -0
- package/configurations/library/drizzle/rules/DRIZZLE.md +44 -0
- package/configurations/library/react-hook-form/manifest.toml +15 -0
- package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +48 -0
- package/configurations/library/swift-snapshot-testing/manifest.toml +32 -0
- package/configurations/library/tanstack-query/eslint-presets.json +21 -0
- package/configurations/library/tanstack-query/manifest.toml +26 -0
- package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +41 -0
- package/configurations/library/testing-library/eslint-presets.json +191 -0
- package/configurations/library/testing-library/manifest.toml +47 -0
- package/configurations/library/translations/manifest.toml +40 -0
- package/configurations/library/translations/rules/NEXT-INTL.md +33 -0
- package/configurations/library/translations/rules/TRANSLATIONS.md +102 -0
- package/configurations/library/trpc/manifest.toml +14 -0
- package/configurations/library/trpc/rules/TRPC.md +43 -0
- package/configurations/library/zod/manifest.toml +25 -0
- package/configurations/library/zod/rules/ZOD.md +44 -0
- package/configurations/library/zustand/manifest.toml +18 -0
- package/configurations/library/zustand/rules/ZUSTAND.md +106 -0
- package/configurations/platform/cloudflare/manifest.toml +94 -0
- package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +23 -0
- package/configurations/platform/cloudflare/semgrep/cloudflare.yml +12 -0
- package/configurations/platform/supabase/manifest.toml +112 -0
- package/configurations/platform/supabase/rules/SUPABASE.md +129 -0
- package/configurations/platform/supabase/semgrep/supabase.yml +60 -0
- package/configurations/testing/jest/manifest.toml +54 -0
- package/configurations/testing/pytest/manifest.toml +53 -0
- package/configurations/testing/swift-tests/manifest.toml +52 -0
- package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +79 -0
- package/configurations/testing/vitest/eslint-presets.json +22 -0
- package/configurations/testing/vitest/manifest.toml +69 -0
- package/configurations/testing/vitest/rules/VITEST.md +18 -0
- package/dist/LICENSE.md +201 -0
- package/dist/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/eslint-rule-levels.json +10 -0
- package/dist/gspot.js +30740 -0
- package/dist/linkinator-program.js +65 -0
- package/dist/purgecss-program.js +51 -0
- package/dist/tool-versions.json +3 -0
- package/grammars/bash.wasm +0 -0
- package/grammars/css.wasm +0 -0
- package/grammars/html.wasm +0 -0
- package/grammars/javascript.wasm +0 -0
- package/grammars/licenses/tree-sitter-bash.txt +21 -0
- package/grammars/licenses/tree-sitter-css.txt +21 -0
- package/grammars/licenses/tree-sitter-html.txt +21 -0
- package/grammars/licenses/tree-sitter-javascript.txt +21 -0
- package/grammars/licenses/tree-sitter-python.txt +21 -0
- package/grammars/licenses/tree-sitter-swift.txt +21 -0
- package/grammars/licenses/tree-sitter-typescript.txt +21 -0
- package/grammars/python.wasm +0 -0
- package/grammars/swift.wasm +0 -0
- package/grammars/tsx.wasm +0 -0
- package/grammars/typescript.wasm +0 -0
- package/gspot.schema.json +45991 -0
- package/package.json +93 -5
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# OpenAPI
|
|
2
|
+
|
|
3
|
+
## Public contracts
|
|
4
|
+
|
|
5
|
+
When the project publishes OpenAPI, its document describes the runtime API.
|
|
6
|
+
|
|
7
|
+
- Keep required fields, types, accepted media types, status codes, and security requirements
|
|
8
|
+
consistent between the document and the implementation.
|
|
9
|
+
- Preserve the chosen schema library and the command that writes the OpenAPI document.
|
|
10
|
+
- Keep request and response examples valid against their schemas.
|
|
11
|
+
- Document error responses and nullable or optional values precisely.
|
|
12
|
+
|
|
13
|
+
This OpenAPI 3.1 fragment describes a query limit. Its bounds and default must also hold
|
|
14
|
+
when the server handles the request:
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
parameters:
|
|
18
|
+
- name: limit
|
|
19
|
+
in: query
|
|
20
|
+
required: false
|
|
21
|
+
schema:
|
|
22
|
+
type: integer
|
|
23
|
+
minimum: 1
|
|
24
|
+
maximum: 100
|
|
25
|
+
default: 50
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Contract tests
|
|
29
|
+
|
|
30
|
+
When you write contract tests:
|
|
31
|
+
|
|
32
|
+
- Validate representative success and error responses against the published schemas.
|
|
33
|
+
- Exercise invalid body, query, and route values through HTTP and assert their declared errors.
|
|
34
|
+
- Verify authentication and authorization requirements for protected operations.
|
|
35
|
+
- Test the public response. Assert its status and content type.
|
|
36
|
+
- Keep generated schemas and their inputs synchronized through the existing generator.
|
|
37
|
+
|
|
38
|
+
An assertion that checks only selected response fields does not prove that the whole response
|
|
39
|
+
satisfies its schema. Use the project's schema validator for that claim.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
allowed_words = ["Xcode"]
|
|
2
|
+
[configuration]
|
|
3
|
+
title = "Xcode"
|
|
4
|
+
suggests = ["swift"]
|
|
5
|
+
description = "An Xcode project: property lists that parse, build settings files, and complete string catalogs. Asset catalogs hold their images, test targets sit in a plan, sources belong to a target, and entitlements stay on a list."
|
|
6
|
+
|
|
7
|
+
[detect]
|
|
8
|
+
paths = ["**/*.xcodeproj/project.pbxproj"]
|
|
9
|
+
project_files = ["*.xcodeproj"]
|
|
10
|
+
|
|
11
|
+
[files]
|
|
12
|
+
extensions = [".plist", ".entitlements", ".xcconfig", ".xcstrings", ".storyboard", ".xib"]
|
|
13
|
+
paths = ["**/project.pbxproj", "**/*.xcscheme", "**/*.xctestplan", "**/*.xcassets/**/Contents.json"]
|
|
14
|
+
|
|
15
|
+
[[tool]]
|
|
16
|
+
name = "plutil"
|
|
17
|
+
install_hint = "install Xcode from the App Store"
|
|
18
|
+
system = true
|
|
19
|
+
builds = ["macos"]
|
|
20
|
+
crash_pattern = "couldn['’]t be opened"
|
|
21
|
+
|
|
22
|
+
[[check]]
|
|
23
|
+
name = "plutil"
|
|
24
|
+
example = "A plist containing an unclosed dictionary fails native validation. Close the dictionary and plist elements, then rerun with plutil available on a declared platform."
|
|
25
|
+
level = "recommended"
|
|
26
|
+
stage = "commit"
|
|
27
|
+
command = ["plutil", "-lint", "{files}"]
|
|
28
|
+
files = { extensions = [".plist", ".entitlements"] }
|
|
29
|
+
output = { format = "regex", pattern = "^(?<file>[^:]+): (?<message>(?!OK$).+)$" }
|
|
30
|
+
summary = "Parses every property list and entitlements file with plutil."
|
|
31
|
+
why = "A broken plist fails the build or, worse, ships an app whose settings silently fell back to defaults."
|
|
32
|
+
help = "Fix the element plutil names."
|
|
33
|
+
|
|
34
|
+
[[check]]
|
|
35
|
+
name = "xcconfig"
|
|
36
|
+
example = "A non-comment line `PRODUCT_NAME App` in an `.xcconfig` file lacks an assignment operator. Change it to `PRODUCT_NAME = App`."
|
|
37
|
+
level = "recommended"
|
|
38
|
+
stage = "commit"
|
|
39
|
+
runs = "scope"
|
|
40
|
+
files = { extensions = [".xcconfig"] }
|
|
41
|
+
summary = "Checks that every line of a build settings file is a setting, an include, or a comment."
|
|
42
|
+
why = "Xcode ignores a line it cannot read, so a typo turns a setting off without a word."
|
|
43
|
+
help = "Write the line as a name, an equals sign, and a value."
|
|
44
|
+
|
|
45
|
+
[[check]]
|
|
46
|
+
name = "xcstrings"
|
|
47
|
+
example = "A string catalog entry with one translated locale and an absent required locale reports the missing translation. Add that locale and its translation, or explicitly mark the string as not translated when that is the intended catalog state."
|
|
48
|
+
level = "recommended"
|
|
49
|
+
stage = "commit"
|
|
50
|
+
runs = "scope"
|
|
51
|
+
files = { extensions = [".xcstrings"] }
|
|
52
|
+
summary = "Checks that every string catalog parses, and that every string has a translation for every locale the catalog uses."
|
|
53
|
+
why = "A string with no translation shows its source language in the middle of a translated screen."
|
|
54
|
+
help = "Add the translation, or mark the string as not translated in the catalog."
|
|
55
|
+
|
|
56
|
+
[[check]]
|
|
57
|
+
name = "assets"
|
|
58
|
+
example = "A `Contents.json` image entry naming `icon.png` reports a missing asset when that file does not exist. Add the intended image beside the manifest or remove the obsolete entry."
|
|
59
|
+
level = "recommended"
|
|
60
|
+
stage = "commit"
|
|
61
|
+
runs = "scope"
|
|
62
|
+
files = { paths = ["**/*.xcassets/**"], extensions = [".swift"] }
|
|
63
|
+
summary = "Checks that every asset Contents.json parses and that every image set holds the images it names."
|
|
64
|
+
why = "An image set with no image draws nothing at run time."
|
|
65
|
+
help = "Add the image or delete the asset."
|
|
66
|
+
|
|
67
|
+
[[check]]
|
|
68
|
+
name = "unused-assets"
|
|
69
|
+
example = "An asset set named only in an Xcode build setting counts as used. An asset that no source or build setting names reports an unused-asset finding."
|
|
70
|
+
level = "all"
|
|
71
|
+
stage = "commit"
|
|
72
|
+
runs = "scope"
|
|
73
|
+
files = { paths = ["**/*.xcassets/**"], extensions = [".swift", ".storyboard", ".xib", ".plist", ".pbxproj", ".xcconfig"] }
|
|
74
|
+
summary = "At level all, finds asset sets that no source or Xcode build setting names."
|
|
75
|
+
why = "An asset nobody names ships in the app for nothing."
|
|
76
|
+
help = "Delete the asset or name it where it is used. For an accepted asset selected at runtime, run gspot ignore xcode/unused-assets --rule unused-asset --paths `<glob>` --reason \"`<why>`\"."
|
|
77
|
+
|
|
78
|
+
[[check]]
|
|
79
|
+
name = "test-plans"
|
|
80
|
+
example = "A test target absent from the test plan used by its scheme reports missing coverage by the plan. Add that target to the plan and point the scheme at the plan."
|
|
81
|
+
level = "all"
|
|
82
|
+
stage = "commit"
|
|
83
|
+
runs = "scope"
|
|
84
|
+
summary = "Checks that every shared scheme that runs tests names a test plan, and that every test target is in a plan."
|
|
85
|
+
why = "A test target outside every plan never runs in CI, and its tests rot while they look present."
|
|
86
|
+
help = "Add the target to a test plan, and point the scheme at the plan."
|
|
87
|
+
|
|
88
|
+
[[check]]
|
|
89
|
+
name = "files-without-target"
|
|
90
|
+
example = "A Swift source file under the project that belongs to no target reports an orphan source. Add the file to its intended target or delete the unused source. Remove project references to files that no longer exist."
|
|
91
|
+
level = "recommended"
|
|
92
|
+
stage = "commit"
|
|
93
|
+
runs = "scope"
|
|
94
|
+
files = { extensions = [".swift"], paths = ["**/project.pbxproj"] }
|
|
95
|
+
summary = "Compares the Swift files in the tree with the files the project names: a file in no target, and a target file that is gone."
|
|
96
|
+
why = "A Swift file in no target is never compiled, so it breaks without anyone seeing it."
|
|
97
|
+
help = "Add the file to a target or delete it, and remove the reference to a file that is gone."
|
|
98
|
+
|
|
99
|
+
[[check]]
|
|
100
|
+
name = "symlinks"
|
|
101
|
+
when = { git = true }
|
|
102
|
+
example = "A source directory represented by a symlink reports a project symlink finding. Replace the link with the intended repository-owned directory and update project references."
|
|
103
|
+
level = "all"
|
|
104
|
+
stage = "commit"
|
|
105
|
+
runs = "scope"
|
|
106
|
+
summary = "Reports every tracked symlink beside or under an Xcode project, with where it points."
|
|
107
|
+
why = "Xcode, git, and each lint tool follow a symlink their own way, so one file is checked twice or never."
|
|
108
|
+
help = "Replace the symlink with the folder it points at, or ignore the path with a reason."
|
|
109
|
+
|
|
110
|
+
[[check]]
|
|
111
|
+
name = "entitlements"
|
|
112
|
+
example = "An entitlement enabled in an `.entitlements` file but absent from `xcode.allowed_entitlements` reports a settings finding. Remove an unnecessary entitlement, or explicitly allow the required entitlement in settings."
|
|
113
|
+
level = "recommended"
|
|
114
|
+
when = { setting = "xcode.allowed_entitlements" }
|
|
115
|
+
stage = "commit"
|
|
116
|
+
runs = "scope"
|
|
117
|
+
files = { extensions = [".entitlements"] }
|
|
118
|
+
summary = "Checks that every entitlement is on xcode.allowed_entitlements, when the settings hold that list."
|
|
119
|
+
why = "An entitlement is a capability the system grants the app, and a new one deserves a review of its own."
|
|
120
|
+
help = "Remove the entitlement, or add it to xcode.allowed_entitlements."
|
|
121
|
+
|
|
122
|
+
[[check]]
|
|
123
|
+
name = "app-transport-security"
|
|
124
|
+
example = "An Info.plist enabling `NSAllowsArbitraryLoads` disables transport protection broadly and reports a finding. Remove that key. If one host requires an exception, declare only the host-specific exception."
|
|
125
|
+
level = "recommended"
|
|
126
|
+
stage = "commit"
|
|
127
|
+
runs = "scope"
|
|
128
|
+
files = { extensions = [".plist"] }
|
|
129
|
+
summary = "Refuses a property list that allows arbitrary loads, which turns transport security off for every host."
|
|
130
|
+
why = "With arbitrary loads the app speaks plain HTTP to anyone, and one forgotten debug setting ships it."
|
|
131
|
+
help = "Remove the key, and add an exception for the one host that needs it."
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Xcode
|
|
2
|
+
|
|
3
|
+
## Project settings
|
|
4
|
+
|
|
5
|
+
- `SWIFT_STRICT_CONCURRENCY = complete`. Set the upcoming-feature flags the project adopts in
|
|
6
|
+
the project, not per file.
|
|
7
|
+
- Share schemes needed by collaborators and CI. Keep personal scheme state out of the repository.
|
|
8
|
+
- Set deployment targets deliberately for each supported product. Verify compatibility
|
|
9
|
+
between apps, extensions, frameworks, and package dependencies.
|
|
10
|
+
|
|
11
|
+
## Files
|
|
12
|
+
|
|
13
|
+
- Keep source and resource membership consistent with the intended targets. Avoid duplicate
|
|
14
|
+
build entries; preserve synchronized groups where the project uses them.
|
|
15
|
+
- Put images and named colors in asset catalogs when the platform supports their required format.
|
|
16
|
+
Keep other resources in the appropriate bundle locations.
|
|
17
|
+
- `Info.plist` values that vary by configuration come from build settings
|
|
18
|
+
such as `$(PRODUCT_BUNDLE_IDENTIFIER)`, not from edited plist copies.
|
|
19
|
+
|
|
20
|
+
## Build phases and scripts
|
|
21
|
+
|
|
22
|
+
- Run-script phases declare their interpreter and pass the checks for that language.
|
|
23
|
+
- Every run-script phase declares its input and output files so the build system can skip it.
|
|
24
|
+
- No network access or code generation without declared outputs.
|
|
25
|
+
- The linter and the formatter run before commit, not as build phases that fail the build twice.
|
|
26
|
+
|
|
27
|
+
## Project organization
|
|
28
|
+
|
|
29
|
+
<!-- level: all -->
|
|
30
|
+
|
|
31
|
+
Keep shared build settings in the project or `.xcconfig` file. A target overrides only
|
|
32
|
+
what differs. Preserve the project's chosen modules and packages; do not introduce
|
|
33
|
+
a package only to enforce a directory layout. Keep substantial build-script logic in a
|
|
34
|
+
repository file its language tools can check.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
allowed_words = ["Bash", "ShellCheck", "tree-sitter"]
|
|
2
|
+
[configuration]
|
|
3
|
+
title = "Bash, zsh, and Bats scripts"
|
|
4
|
+
suggests = []
|
|
5
|
+
description = "Shell scripts, hooks and task files: ShellCheck, shfmt, a syntax pass, and the structure rules for shell."
|
|
6
|
+
|
|
7
|
+
[detect]
|
|
8
|
+
extensions = [".sh", ".bash", ".zsh", ".bats"]
|
|
9
|
+
shebangs = ["shell"]
|
|
10
|
+
|
|
11
|
+
[files]
|
|
12
|
+
extensions = [".sh", ".bash", ".zsh", ".bats"]
|
|
13
|
+
tags = ["shebang:shell"]
|
|
14
|
+
|
|
15
|
+
[[tool]]
|
|
16
|
+
name = "shellcheck"
|
|
17
|
+
refused_options = [
|
|
18
|
+
{ paths = ["enable", "disable"], message = "ShellCheck rule selection comes from level recommended or all and gspot ignore. Remove enable and disable from tools.shellcheck.extra.value." },
|
|
19
|
+
]
|
|
20
|
+
rule_url = "https://www.shellcheck.net/wiki/{rule}"
|
|
21
|
+
suppression = { marker = "\\bshellcheck +disable=", reason = "# ?reason: (?<reason>\\S.*)" }
|
|
22
|
+
version = "0.11.0"
|
|
23
|
+
mise = "shellcheck"
|
|
24
|
+
brew = "shellcheck"
|
|
25
|
+
apt = "shellcheck"
|
|
26
|
+
winget = "koalaman.shellcheck"
|
|
27
|
+
scoop = "shellcheck"
|
|
28
|
+
github = { name = "koalaman/shellcheck", version = "v0.11.0" }
|
|
29
|
+
[[tool.replace]]
|
|
30
|
+
file = ".shellcheckrc"
|
|
31
|
+
shared = false
|
|
32
|
+
|
|
33
|
+
[[tool]]
|
|
34
|
+
name = "shfmt"
|
|
35
|
+
version = "3.12.0"
|
|
36
|
+
mise = "shfmt"
|
|
37
|
+
brew = "shfmt"
|
|
38
|
+
winget = "mvdan.shfmt"
|
|
39
|
+
scoop = "shfmt"
|
|
40
|
+
github = { name = "mvdan/sh", version = "v3.12.0" }
|
|
41
|
+
|
|
42
|
+
[[tool]]
|
|
43
|
+
name = "bash"
|
|
44
|
+
min_version = "4.4"
|
|
45
|
+
install_hint = "install Bash 4.4 or newer, such as with brew install bash on macOS"
|
|
46
|
+
system = true
|
|
47
|
+
version_command = ["--version"]
|
|
48
|
+
version_pattern = "version (\\d+\\.\\d+(?:\\.\\d+)?)"
|
|
49
|
+
|
|
50
|
+
[[tool]]
|
|
51
|
+
name = "zsh"
|
|
52
|
+
system = true
|
|
53
|
+
|
|
54
|
+
[[tool]]
|
|
55
|
+
name = "bats"
|
|
56
|
+
system = true
|
|
57
|
+
|
|
58
|
+
[[tool_file]]
|
|
59
|
+
target = ".gspot/config/shellcheckrc"
|
|
60
|
+
tool = "shellcheck"
|
|
61
|
+
rule_keys = ["enable", "disable"]
|
|
62
|
+
per_scope = true
|
|
63
|
+
|
|
64
|
+
[[check]]
|
|
65
|
+
name = "bash-syntax"
|
|
66
|
+
example = "In `greet.sh`, `if then` has no condition. Use a real test: `if [ -f \"$file\" ]; then printf \"%s\\n\" \"$file\"; fi`, then rerun the syntax check."
|
|
67
|
+
level = "recommended"
|
|
68
|
+
stage = "commit"
|
|
69
|
+
command = ["bash", "-n", "{file}"]
|
|
70
|
+
files = { tags = ["bash"] }
|
|
71
|
+
output = { format = "regex", pattern = "^.*: line (?<line>\\d+): (?<message>.*)$" }
|
|
72
|
+
summary = "Checks that Bash scripts parse before running them."
|
|
73
|
+
why = "A script with a syntax error stops at the broken line, sometimes after it has already done half its work."
|
|
74
|
+
help = "Open the file at the line bash names and fix the quoting, bracket, or keyword it complains about."
|
|
75
|
+
|
|
76
|
+
[[check]]
|
|
77
|
+
name = "zsh-syntax"
|
|
78
|
+
example = "In a Zsh script, `if then` has no condition. Use a real test: `if [[ -f \"$file\" ]]; then print -r -- \"$file\"; fi`, then rerun with Zsh installed."
|
|
79
|
+
level = "recommended"
|
|
80
|
+
stage = "commit"
|
|
81
|
+
command = ["zsh", "-n", "{file}"]
|
|
82
|
+
files = { tags = ["zsh"] }
|
|
83
|
+
output = { format = "regex", pattern = "^(?<file>[^:]+):(?<line>\\d+):\\s*(?<message>.*)$" }
|
|
84
|
+
summary = "Checks Zsh syntax with the Zsh interpreter."
|
|
85
|
+
why = "Zsh syntax differs from Bash, so a Bash parser rejects valid Zsh programs."
|
|
86
|
+
help = "Fix the syntax at the line Zsh reports. Install zsh when the command is missing."
|
|
87
|
+
|
|
88
|
+
[[check]]
|
|
89
|
+
name = "bats-syntax"
|
|
90
|
+
example = "In a `.bats` file, an unterminated `@test \"greeting\" {` block fails parsing. Complete the test body and close it with `}` before rerunning with Bats installed."
|
|
91
|
+
level = "recommended"
|
|
92
|
+
stage = "commit"
|
|
93
|
+
command = ["bats", "--count", "{file}"]
|
|
94
|
+
# Bats parses the file with the bash on PATH, and under Bash 3.2 it passes a file that does not parse.
|
|
95
|
+
tool = ["bats", "bash"]
|
|
96
|
+
files = { tags = ["bats"] }
|
|
97
|
+
output = { format = "regex", pattern = "^# (?<file>[^:]+): line (?<line>\\d+): (?<message>.*)$" }
|
|
98
|
+
summary = "Parses Bats test files while counting their test cases."
|
|
99
|
+
why = "Bats test declarations need the Bats parser before Bash can read them."
|
|
100
|
+
help = "Fix the syntax reported by Bats. Install bats-core when the command is missing."
|
|
101
|
+
|
|
102
|
+
[[check]]
|
|
103
|
+
name = "shellcheck"
|
|
104
|
+
example = "In a Bash script, `printf \"%s\\n\" $name` reports SC2086 because the expansion permits word splitting and globbing. Use `printf \"%s\\n\" \"$name\"` to pass the value as one argument."
|
|
105
|
+
level = "recommended"
|
|
106
|
+
stage = "commit"
|
|
107
|
+
files = { tags = ["bash", "sh", "bats"] }
|
|
108
|
+
command = ["shellcheck", "--rcfile", "{tool_file:shellcheckrc}", "--severity=style", "--check-sourced", "--format=gcc", "{files}"]
|
|
109
|
+
exit_codes = [1]
|
|
110
|
+
output = { format = "regex", pattern = "^(?<file>[^:]+):(?<line>\\d+):(?<column>\\d+): (?:note|warning|error|style): (?<message>.*?) \\[(?<rule>SC\\d+)\\]$" }
|
|
111
|
+
summary = "Runs ShellCheck over shell scripts and sourced files, adding stable optional ShellCheck rules at level all."
|
|
112
|
+
why = "ShellCheck catches the quoting, globbing and exit-status mistakes that make shell scripts fail on the one input nobody tested."
|
|
113
|
+
help = "Read the SC code in the finding; gspot explain shellcheck/SC2086 links to its page. Turn one code off with gspot ignore bash/shellcheck --rule SC2086 --reason \"`<why>`\"."
|
|
114
|
+
|
|
115
|
+
[[check]]
|
|
116
|
+
name = "shfmt"
|
|
117
|
+
example = "In a script, `if true;then echo hello;fi` differs from shfmt output. Run the configured formatter to add the required spacing and block layout, then rerun the check."
|
|
118
|
+
level = "recommended"
|
|
119
|
+
stage = "commit"
|
|
120
|
+
files = { tags = ["bash", "sh", "bats"] }
|
|
121
|
+
command = ["shfmt", "-d", "-i", "{indent}", "-ci", "-s", "{files}"]
|
|
122
|
+
fix = ["shfmt", "-w", "-i", "{indent}", "-ci", "-s", "{files}"]
|
|
123
|
+
output = { format = "regex", pattern = "^(?:--- )?(?<file>.+?)(?:\\.orig$|:(?<line>\\d+):(?<column>\\d+): (?<message>.+)$)", message = "This script is not formatted the way shfmt formats it.", fixable = "^--- " }
|
|
124
|
+
summary = "Checks that every shell script is formatted the way shfmt formats it."
|
|
125
|
+
why = "One formatter means diffs show what changed, not how someone's editor indents."
|
|
126
|
+
help = "Run gspot check --fix to format every script, or shfmt -w on one file."
|
|
127
|
+
|
|
128
|
+
[[check]]
|
|
129
|
+
name = "script-layout"
|
|
130
|
+
example = "An executable Bash script without `set -euo pipefail` reports a strict-mode finding. Put strict mode before the first command and, when the script defines functions, call the documented `main` function last."
|
|
131
|
+
level = "all"
|
|
132
|
+
stage = "commit"
|
|
133
|
+
summary = "Checks the contract every Bash script keeps: the shebang, strict mode, a main called last when functions are defined, declarative libraries, cleaned-up temporary files."
|
|
134
|
+
why = "A script that skips strict mode or hides its runtime fails on the machine where it matters, silently."
|
|
135
|
+
help = "Open with the shebang and run set -euo pipefail first. When an executable defines functions, end it with a call to main."
|
|
136
|
+
|
|
137
|
+
[[check]]
|
|
138
|
+
name = "doc-comments"
|
|
139
|
+
example = "A function `_quiet() { printf \"%s\\n\" \"Hello\"; }` without a preceding description reports a missing comment. Add `# _quiet: prints a greeting.` immediately above the function. Other structural checks still apply."
|
|
140
|
+
level = "all"
|
|
141
|
+
stage = "commit"
|
|
142
|
+
summary = "Checks that every shell function announces itself in a comment above it that says more than its name."
|
|
143
|
+
why = "A function with no summary makes the reader run it in their head; a vague summary is a summary nobody will trust."
|
|
144
|
+
help = "Write \"# name: what it does\" above the function; when it carries doc sections, keep Globals, Arguments, Outputs, Returns in that order."
|
|
145
|
+
|
|
146
|
+
[[check]]
|
|
147
|
+
name = "unused-functions"
|
|
148
|
+
example = "A private shell function `_orphan` that no script calls reports an unused-function finding. Delete the unused definition. If an external hook requires the function, record that contract with `gspot ignore` and a reason."
|
|
149
|
+
level = "recommended"
|
|
150
|
+
stage = "commit"
|
|
151
|
+
runs = "scope"
|
|
152
|
+
summary = "Finds shell functions no script in the scope calls."
|
|
153
|
+
why = "ShellCheck checks one file; this check follows calls across every script in the scope."
|
|
154
|
+
help = "Delete the function. For a required hook entry point, run `gspot ignore bash/unused-functions --paths <file> --reason <why>`."
|
|
155
|
+
|
|
156
|
+
[[check]]
|
|
157
|
+
name = "unread-arguments"
|
|
158
|
+
example = "A call `_deaf one two` whose function never reads positional arguments reports dead parameters. If both arguments are unnecessary, remove them from the call. Otherwise read and use the arguments in the function."
|
|
159
|
+
level = "recommended"
|
|
160
|
+
stage = "commit"
|
|
161
|
+
runs = "scope"
|
|
162
|
+
summary = "Finds shell functions called with arguments they never read."
|
|
163
|
+
why = "A caller that passes an argument expects it to affect the result. An unread argument misleads the caller."
|
|
164
|
+
help = "Read the argument in the function, or drop it from every call."
|
|
165
|
+
|
|
166
|
+
[[check]]
|
|
167
|
+
name = "source-order"
|
|
168
|
+
example = "A block of `source` lines that is not ordered shortest first reports the sorted order. Reorder the lines."
|
|
169
|
+
level = "all"
|
|
170
|
+
stage = "commit"
|
|
171
|
+
summary = "Checks that the source statements of a block go shortest first, alphabetically among lines of one length."
|
|
172
|
+
why = "One order for every block means a reader never searches it."
|
|
173
|
+
help = "Reorder the source statements shortest first."
|
|
174
|
+
|
|
175
|
+
[[check]]
|
|
176
|
+
name = "private-prefix"
|
|
177
|
+
example = "A function `build_it` called only from its own script reports a missing private prefix. Rename it `_build_it` and update each local call. A function called from another script keeps its public name."
|
|
178
|
+
level = "all"
|
|
179
|
+
stage = "commit"
|
|
180
|
+
runs = "scope"
|
|
181
|
+
summary = "Checks that a shell function no other file calls starts with an underscore, and that an underscore function is not called from outside."
|
|
182
|
+
why = "The underscore tells a reader what the file publishes without reading every caller."
|
|
183
|
+
help = "Rename the function with a leading underscore, or drop the underscore when another file calls it."
|
|
184
|
+
|
|
185
|
+
[[check]]
|
|
186
|
+
name = "function-size"
|
|
187
|
+
example = "A function over `limits.bash.function_lines`, `limits.bash.branches`, `limits.bash.nesting`, or `limits.bash.assignments` reports the count and the ceiling under `function-lines`, `branches`, `nesting`, or `assignments`. Split the function by the job, then rerun."
|
|
188
|
+
level = "all"
|
|
189
|
+
stage = "commit"
|
|
190
|
+
summary = "Checks every shell function against the size ceilings: code lines, branches, nesting, and assignments."
|
|
191
|
+
why = "A long script holds more than one job, and a function with many branches or assignments is several functions with one name."
|
|
192
|
+
help = "Split the script or the function, or raise the named limits.bash setting with a reason."
|
|
193
|
+
|
|
194
|
+
[[check]]
|
|
195
|
+
name = "wrappers"
|
|
196
|
+
example = "A short launcher containing only `exec bash scripts/build.sh` reports a forwarding-wrapper finding. Invoke `scripts/build.sh` directly and remove the redundant launcher."
|
|
197
|
+
level = "all"
|
|
198
|
+
stage = "commit"
|
|
199
|
+
summary = "Finds forwarding wrappers and compatibility aliases in shell scripts."
|
|
200
|
+
why = "A wrapper is a second entry point that drifts from the first."
|
|
201
|
+
help = "Call the target script directly and delete the redundant launcher or alias."
|
|
202
|
+
|
|
203
|
+
[[check]]
|
|
204
|
+
name = "inline-scripts"
|
|
205
|
+
example = "A shell heredoc containing a Python program reports embedded code. Move the program into a `.py` file and invoke that file from the shell script."
|
|
206
|
+
level = "all"
|
|
207
|
+
stage = "commit"
|
|
208
|
+
summary = "Finds inline Python, Node, and generated-script heredocs in shell scripts."
|
|
209
|
+
why = "Code inside a heredoc is checked by nothing."
|
|
210
|
+
help = "Move the embedded code into its own file and run that."
|
|
211
|
+
|
|
212
|
+
[[check]]
|
|
213
|
+
name = "ssh-blocks"
|
|
214
|
+
example = "An anonymous SSH heredoc reports a remote-block finding. Move the remote operation into a named function with a comment that explains its purpose, then invoke that function through the supported remote call."
|
|
215
|
+
level = "all"
|
|
216
|
+
stage = "commit"
|
|
217
|
+
summary = "Checks that ssh heredocs are documented."
|
|
218
|
+
why = "A remote command block with no name is the hardest thing in a script to test or reuse."
|
|
219
|
+
help = "Move the block into a named function and write \"# name: what it does\" above it."
|
|
220
|
+
|
|
221
|
+
[[check]]
|
|
222
|
+
name = "variable-defaults"
|
|
223
|
+
example = "A default such as `${PORT:-8080}` outside a declared configuration owner reports a misplaced default. Set the default in a path listed in `architecture.roles.env` and consume the configured value elsewhere."
|
|
224
|
+
level = "all"
|
|
225
|
+
stage = "commit"
|
|
226
|
+
summary = "Finds variable defaults of the form name:-value outside the configuration owners."
|
|
227
|
+
why = "A default set in three places is three settings with one name."
|
|
228
|
+
help = "Set the default in a configuration owner named under architecture.roles.env and read the variable everywhere else."
|
|
229
|
+
|
|
230
|
+
[[check]]
|
|
231
|
+
name = "include-guards"
|
|
232
|
+
example = "A sourced configuration owner without a load guard reports a guard finding. Begin it with `[[ -n ${APP_CONFIG_READY:-} ]] && return 0`, followed by `readonly APP_CONFIG_READY=1`, before declaring its configuration."
|
|
233
|
+
level = "all"
|
|
234
|
+
stage = "commit"
|
|
235
|
+
runs = "scope"
|
|
236
|
+
summary = "Checks that each script in architecture.roles.env starts with an include guard of its own."
|
|
237
|
+
why = "A configuration file sourced twice runs its side effects twice."
|
|
238
|
+
help = "Open the owner with [[ -n ${NAME_READY:-} ]] && return 0 and then readonly NAME_READY=1, with a guard name no other owner uses."
|
|
239
|
+
|
|
240
|
+
[[check]]
|
|
241
|
+
name = "safety"
|
|
242
|
+
example = "A `killall worker` command reports broad-kill. Target the specific process, or ignore that rule for the owning script with a reason."
|
|
243
|
+
level = "recommended"
|
|
244
|
+
stage = "commit"
|
|
245
|
+
summary = "Finds broad process kills and recursive deletes, except traps that remove mktemp paths. At all, it also reports discarded failures."
|
|
246
|
+
why = "Each of these is a way a script destroys something it did not mean to and says nothing."
|
|
247
|
+
help = "Handle the failure. For required cleanup, use gspot ignore bash/safety with --rule, --paths and --reason."
|
|
248
|
+
|
|
249
|
+
[[naming.override]]
|
|
250
|
+
paths = ["**"]
|
|
251
|
+
languages = ["bash"]
|
|
252
|
+
categories = ["variables"]
|
|
253
|
+
allowed_names = ["IFS", "OPTARG", "OPTIND", "EUID", "UID", "PPID", "RANDOM", "SECONDS", "LINENO", "BASH_SOURCE", "BASH_REMATCH", "BASH_VERSION", "BASH_VERSINFO", "FUNCNAME", "PIPESTATUS", "REPLY", "CDPATH", "OLDPWD", "SHLVL", "HOSTNAME", "OSTYPE", "MACHTYPE", "COLUMNS", "LINES"]
|
|
254
|
+
reason = "The shell defines these variable names."
|
|
255
|
+
|
|
256
|
+
[[naming.override]]
|
|
257
|
+
paths = ["**"]
|
|
258
|
+
languages = ["bash"]
|
|
259
|
+
categories = ["functions", "variables", "constants", "methods", "attributes"]
|
|
260
|
+
ignored_prefix = "^_+"
|
|
261
|
+
reason = "A leading underscore marks a private shell declaration."
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Bash
|
|
2
|
+
|
|
3
|
+
## Bash scope
|
|
4
|
+
|
|
5
|
+
<!-- level: all -->
|
|
6
|
+
|
|
7
|
+
Bash orchestrates commands as glue code and does not build application logic. Prefer
|
|
8
|
+
small scripts with explicit inputs, explicit outputs, and clear failure behavior.
|
|
9
|
+
Treat every path, argument, environment value, command output, and user input as unsafe until
|
|
10
|
+
quoted, validated, or parsed by a structured tool. A script that builds, deploys, deletes, or
|
|
11
|
+
publishes artifacts makes its inputs, effects, and failure behavior explicit.
|
|
12
|
+
|
|
13
|
+
`set -euo pipefail` does not replace checks around dangerous commands. Keep CI YAML and package
|
|
14
|
+
scripts short. Put longer logic in a script file the checks can lint.
|
|
15
|
+
|
|
16
|
+
## Bash tasks
|
|
17
|
+
|
|
18
|
+
<!-- level: all -->
|
|
19
|
+
|
|
20
|
+
Use Bash when the script mostly calls other command-line tools or wires together
|
|
21
|
+
installation, lint, build, runtime, publication, or cleanup steps. Use it to validate the
|
|
22
|
+
environment and dispatch to project commands, or for file movement, process checks,
|
|
23
|
+
and retry loops. Do not use Bash for business logic or complex text parsing. Do not use it
|
|
24
|
+
for JSON, YAML, XML, or HTML transformations beyond extraction with a dedicated
|
|
25
|
+
parser. Large mutable data structures, long-lived daemons, high-performance work,
|
|
26
|
+
security-sensitive parsing of untrusted input, and behavior that needs typed contracts belong
|
|
27
|
+
elsewhere. A workflow that needs nested maps, large
|
|
28
|
+
arrays, state machines, non-trivial validation, complex retries, concurrent work, or domain
|
|
29
|
+
rules exceeds the scope of a script.
|
|
30
|
+
|
|
31
|
+
## Deprecated and forbidden syntax
|
|
32
|
+
|
|
33
|
+
Do not use `function`, `typeset`, `&>`, `|&`, or `for arg; {`. Use `name() { ...; }`,
|
|
34
|
+
`local`, `declare`, `readonly`, explicit redirections, and `for arg in "$@"; do` instead.
|
|
35
|
+
ShellCheck reports `$[ ... ]`, backticks, and `-a` or `-o` inside `[ ... ]`.
|
|
36
|
+
`trap ERR` does not provide general error handling. Check failure where it matters, and reserve traps
|
|
37
|
+
for cleanup that can run safely on the relevant exit path.
|
|
38
|
+
|
|
39
|
+
## Script structure
|
|
40
|
+
|
|
41
|
+
<!-- level: all -->
|
|
42
|
+
|
|
43
|
+
An executable script finishes with a meaningful status: a final diagnostic command, a false
|
|
44
|
+
condition, or an optional cleanup check must not become the script status by accident. Use
|
|
45
|
+
an explicit `exit 0` only when the final command's status differs from the program result and
|
|
46
|
+
prior checks establish success.
|
|
47
|
+
|
|
48
|
+
## Options
|
|
49
|
+
|
|
50
|
+
Use `errexit` as a backstop rather than control flow: it has exceptions in
|
|
51
|
+
conditionals, pipelines, command substitutions, subshells, and functions, so `cd`, `rm`,
|
|
52
|
+
check builds, publishing, uploads, and destructive commands explicitly. Do not toggle
|
|
53
|
+
options around a small operation without restoring the prior state. Do not change `IFS` as a
|
|
54
|
+
strict-mode ritual; set it locally where reading or joining data requires it. Keep `set -x`
|
|
55
|
+
out of committed code.
|
|
56
|
+
|
|
57
|
+
A function called in
|
|
58
|
+
`if`, `while`, `&&`, or `||` writes its checks inside because `errexit` does not stop it there:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# cleanup: Removes the contents of the target directory.
|
|
62
|
+
cleanup() {
|
|
63
|
+
local target="$1"
|
|
64
|
+
|
|
65
|
+
cd -- "${target}" || return 1
|
|
66
|
+
find . -mindepth 1 -maxdepth 1 -exec rm -r -- {} +
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Process substitution hides producer failures; when the producer matters, write to a checked
|
|
71
|
+
temporary file, use `PIPESTATUS`, or call the producer separately. Under `pipefail`, short
|
|
72
|
+
readers such as `head` and `grep -q` can make the producer see SIGPIPE and fail the pipeline;
|
|
73
|
+
consume input predictably or handle the known status. Under `nounset`, use `${name:-}` where
|
|
74
|
+
the public interface accepts an unset variable and `${name:?message}` for a required value at a clear
|
|
75
|
+
boundary. Use `${1:-}` where callers can omit an argument.
|
|
76
|
+
|
|
77
|
+
## Output, logging, and errors
|
|
78
|
+
|
|
79
|
+
Write diagnostics with `printf`, not `echo`. Return or exit with the intended status: printing
|
|
80
|
+
an error does not make a command fail.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# log_status: Prints a timestamped diagnostic line to stderr.
|
|
84
|
+
# Arguments:
|
|
85
|
+
# Step name.
|
|
86
|
+
# Target name.
|
|
87
|
+
# Status label.
|
|
88
|
+
log_status() {
|
|
89
|
+
local step_name="$1"
|
|
90
|
+
local target_name="$2"
|
|
91
|
+
local status_label="$3"
|
|
92
|
+
local timestamp
|
|
93
|
+
|
|
94
|
+
timestamp="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" || return 1
|
|
95
|
+
printf 'ts=%s script=%s step=%s target=%s status=%s\n' \
|
|
96
|
+
"${timestamp}" "${0##*/}" "${step_name}" "${target_name}" \
|
|
97
|
+
"${status_label}" >&2
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Comments and documentation
|
|
102
|
+
|
|
103
|
+
<!-- level: all -->
|
|
104
|
+
|
|
105
|
+
A public, sourced, complex, or risky function carries the full header with `Globals`,
|
|
106
|
+
`Arguments`, `Outputs`, and `Returns` sections.
|