docopslab-dev 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.adoc +645 -318
- data/docopslab-dev.gemspec +2 -3
- data/docs/agent/index.md +4 -8
- data/docs/agent/misc/bash-styles.md +470 -0
- data/docs/agent/missions/conduct-release.md +161 -87
- data/docs/agent/missions/setup-new-project.md +228 -134
- data/docs/agent/roles/devops-release-engineer.md +60 -17
- data/docs/agent/roles/docops-engineer.md +84 -20
- data/docs/agent/roles/planner-architect.md +22 -0
- data/docs/agent/roles/product-engineer.md +63 -15
- data/docs/agent/roles/product-manager.md +57 -24
- data/docs/agent/roles/project-manager.md +48 -12
- data/docs/agent/roles/qa-testing-engineer.md +48 -14
- data/docs/agent/roles/tech-docs-manager.md +63 -17
- data/docs/agent/roles/tech-writer.md +68 -14
- data/docs/agent/skills/asciidoc.md +65 -238
- data/docs/agent/skills/bash-cli-dev.md +135 -0
- data/docs/agent/skills/code-commenting.md +143 -106
- data/docs/agent/skills/fix-broken-links.md +145 -100
- data/docs/agent/skills/fix-jekyll-asciidoc-build-errors.md +1 -10
- data/docs/agent/skills/fix-spelling-issues.md +0 -3
- data/docs/agent/skills/git.md +69 -34
- data/docs/agent/skills/github-issues.md +110 -71
- data/docs/agent/skills/rake-cli-dev.md +1 -1
- data/docs/agent/skills/readme-driven-dev.md +1 -0
- data/docs/agent/skills/release-history.md +1 -7
- data/docs/agent/skills/ruby.md +18 -7
- data/docs/agent/skills/schemagraphy-sgyml.md +3 -0
- data/docs/agent/skills/tests-running.md +22 -14
- data/docs/agent/skills/tests-writing.md +51 -28
- data/docs/agent/skills/write-the-docs.md +71 -9
- data/docs/agent/topics/common-project-paths.md +122 -70
- data/docs/agent/topics/dev-tooling-usage.md +70 -77
- data/docs/agent/topics/devops-ci-cd.md +3 -1
- data/docs/agent/topics/product-docs-deployment.md +18 -12
- data/docs/library-readme.adoc +39 -0
- data/lib/docopslab/dev/cast_ops.rb +199 -0
- data/lib/docopslab/dev/config_manager.rb +6 -6
- data/lib/docopslab/dev/data_utils.rb +42 -0
- data/lib/docopslab/dev/file_utils.rb +18 -7
- data/lib/docopslab/dev/git_branch.rb +201 -0
- data/lib/docopslab/dev/git_hooks.rb +17 -11
- data/lib/docopslab/dev/initializer.rb +13 -4
- data/lib/docopslab/dev/library/cache.rb +167 -0
- data/lib/docopslab/dev/library/fetch.rb +209 -0
- data/lib/docopslab/dev/library.rb +328 -0
- data/lib/docopslab/dev/linters.rb +63 -12
- data/lib/docopslab/dev/manifest.rb +28 -0
- data/lib/docopslab/dev/paths.rb +0 -17
- data/lib/docopslab/dev/script_manager.rb +12 -6
- data/lib/docopslab/dev/skim.rb +109 -0
- data/lib/docopslab/dev/spell_check.rb +2 -2
- data/lib/docopslab/dev/sync_ops.rb +94 -33
- data/lib/docopslab/dev/tasks.rb +58 -18
- data/lib/docopslab/dev/version.rb +1 -1
- data/lib/docopslab/dev.rb +75 -35
- data/specs/data/default-manifest.yml +15 -5
- data/specs/data/library-index.yml +22 -0
- data/specs/data/manifest-schema.yaml +142 -4
- data/specs/data/tasks-def.yml +122 -10
- metadata +28 -39
- data/assets/config-packs/actionlint/base.yml +0 -13
- data/assets/config-packs/actionlint/project.yml +0 -13
- data/assets/config-packs/htmlproofer/base.yml +0 -27
- data/assets/config-packs/htmlproofer/project.yml +0 -25
- data/assets/config-packs/rubocop/base.yml +0 -130
- data/assets/config-packs/rubocop/project.yml +0 -8
- data/assets/config-packs/shellcheck/base.shellcheckrc +0 -14
- data/assets/config-packs/subtxt/ai-asciidoc-antipatterns.sub.txt +0 -11
- data/assets/config-packs/vale/asciidoc/ExplicitSectionIDs.yml +0 -8
- data/assets/config-packs/vale/asciidoc/ExtraLineBeforeLevel1.yml +0 -7
- data/assets/config-packs/vale/asciidoc/OneSentencePerLine.yml +0 -8
- data/assets/config-packs/vale/asciidoc/PreferSourceBlocks.yml +0 -8
- data/assets/config-packs/vale/asciidoc/ProperAdmonitions.yml +0 -8
- data/assets/config-packs/vale/asciidoc/ProperDLs.yml +0 -7
- data/assets/config-packs/vale/asciidoc/UncleanListStart.yml +0 -8
- data/assets/config-packs/vale/authoring/ButParagraph.yml +0 -8
- data/assets/config-packs/vale/authoring/ExNotEg.yml +0 -8
- data/assets/config-packs/vale/authoring/LiteralTerms.yml +0 -20
- data/assets/config-packs/vale/authoring/Spelling.yml +0 -679
- data/assets/config-packs/vale/base.ini +0 -38
- data/assets/config-packs/vale/config/scripts/ExplicitSectionIDs.tengo +0 -56
- data/assets/config-packs/vale/config/scripts/ExtraLineBeforeLevel1.tengo +0 -121
- data/assets/config-packs/vale/config/scripts/OneSentencePerLine.tengo +0 -53
- data/assets/config-packs/vale/project.ini +0 -5
- data/assets/hooks/pre-commit +0 -63
- data/assets/hooks/pre-push +0 -72
- data/assets/scripts/adoc_section_ids.rb +0 -50
- data/assets/scripts/build-common.sh +0 -193
- data/assets/scripts/build-docker.sh +0 -64
- data/assets/scripts/build.sh +0 -56
- data/assets/scripts/parse_jekyll_asciidoc_logs.rb +0 -467
- data/assets/templates/Gemfile +0 -7
- data/assets/templates/Rakefile +0 -3
- data/assets/templates/gitignore +0 -69
- data/assets/templates/jekyll-asciidoc-fix.prompt.yml +0 -17
- data/assets/templates/spellcheck.prompt.yml +0 -16
- data/docs/agent/AGENTS.md +0 -229
data/docopslab-dev.gemspec
CHANGED
|
@@ -20,12 +20,10 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
spec.metadata['changelog_uri'] = 'https://github.com/DocOps/lab/blob/main/gems/docopslab-dev/README.adoc'
|
|
21
21
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
22
22
|
|
|
23
|
-
spec.files = Dir.glob('{lib,
|
|
23
|
+
spec.files = Dir.glob('{lib,docs}/**/*') +
|
|
24
24
|
%w[README.adoc LICENSE docopslab-dev.gemspec] +
|
|
25
25
|
Dir.glob('specs/data/*')
|
|
26
26
|
|
|
27
|
-
spec.bindir = 'exe'
|
|
28
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
29
27
|
spec.require_paths = ['lib']
|
|
30
28
|
|
|
31
29
|
# Core runtime dependencies
|
|
@@ -34,6 +32,7 @@ Gem::Specification.new do |spec|
|
|
|
34
32
|
spec.add_dependency 'yaml', '~> 0.2'
|
|
35
33
|
|
|
36
34
|
# Code quality and linting
|
|
35
|
+
spec.add_dependency 'asciisourcerer', '~> 0.2'
|
|
37
36
|
spec.add_dependency 'debride', '~> 1.13'
|
|
38
37
|
spec.add_dependency 'fasterer', '~> 0.11'
|
|
39
38
|
spec.add_dependency 'flog', '~> 4.8'
|
data/docs/agent/index.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# DocOps Lab AI Agent Documentation
|
|
2
2
|
|
|
3
|
-
<section class="docs-group"></section>
|
|
4
|
-
|
|
5
3
|
## Roles
|
|
6
4
|
|
|
7
5
|
#### [AGENT ROLE: Assistant Planner / Project Architect](/docs/agent/planner-architect/)
|
|
@@ -22,8 +20,6 @@
|
|
|
22
20
|
|
|
23
21
|
#### [AGENT ROLE: Technical Writer](/docs/agent/tech-writer/)
|
|
24
22
|
|
|
25
|
-
<section class="docs-group"></section>
|
|
26
|
-
|
|
27
23
|
## Skills
|
|
28
24
|
|
|
29
25
|
#### [AI Agent Instructions for Git Operations](/docs/agent/git/)
|
|
@@ -32,6 +28,10 @@
|
|
|
32
28
|
|
|
33
29
|
#### [Agent Rake CLI Guide](/docs/agent/rake-cli-dev/)
|
|
34
30
|
|
|
31
|
+
#### [Bash CLI Development for Agents](/docs/agent/bash-cli-dev/)
|
|
32
|
+
|
|
33
|
+
#### [Bash Coding for Agents](/docs/agent/bash-styles/)
|
|
34
|
+
|
|
35
35
|
#### [Code Commenting](/docs/agent/code-commenting/)
|
|
36
36
|
|
|
37
37
|
#### [Documenting Product Changes](/docs/agent/write-the-docs/)
|
|
@@ -58,8 +58,6 @@
|
|
|
58
58
|
|
|
59
59
|
#### [Writing Tests for DocOps Lab Projects](/docs/agent/tests-writing/)
|
|
60
60
|
|
|
61
|
-
<section class="docs-group"></section>
|
|
62
|
-
|
|
63
61
|
## Topics
|
|
64
62
|
|
|
65
63
|
#### [AI Agent Instructions for In-house Dev-Tooling Usage](/docs/agent/dev-tooling-usage/)
|
|
@@ -70,8 +68,6 @@
|
|
|
70
68
|
|
|
71
69
|
#### [Product Documentation Deployment](/docs/agent/product-docs-deployment/)
|
|
72
70
|
|
|
73
|
-
<section class="docs-group"></section>
|
|
74
|
-
|
|
75
71
|
## Missions
|
|
76
72
|
|
|
77
73
|
#### [MISSION: Conduct a Product Release](/docs/agent/conduct-release/)
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# Bash Coding for Agents
|
|
2
|
+
|
|
3
|
+
This document is intended for AI agents operating within a DocOps Lab environment.
|
|
4
|
+
|
|
5
|
+
## File and Script Structure
|
|
6
|
+
|
|
7
|
+
### Bash Version (4.0)
|
|
8
|
+
|
|
9
|
+
Use Bash 4.0 or later to take advantage of modern features like associative arrays and improved string manipulation.
|
|
10
|
+
|
|
11
|
+
### Shebang
|
|
12
|
+
|
|
13
|
+
Always start scripts with this canonical shebang.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
#!/usr/bin/env bash
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Script Header
|
|
20
|
+
|
|
21
|
+
Follow the shebang with a brief inline comment block covering the script’s purpose and dependencies. Keep it compact: one line per thought, no visual padding.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
#!/usr/bin/env bash
|
|
25
|
+
# script-name: brief description of what the script does.
|
|
26
|
+
# Depends on: curl, jq
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Indentation
|
|
30
|
+
|
|
31
|
+
Indent with 2 spaces anywhere indentation is called for.
|
|
32
|
+
|
|
33
|
+
Do not use 4 spaces or tabs.
|
|
34
|
+
|
|
35
|
+
### Code Organization
|
|
36
|
+
|
|
37
|
+
Structure your script into logical sections to improve readability.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
#!/usr/bin/env bash
|
|
41
|
+
# script-name: brief description.
|
|
42
|
+
# Depends on: curl, jq
|
|
43
|
+
set -euo pipefail
|
|
44
|
+
|
|
45
|
+
# CONSTANTS
|
|
46
|
+
readonly SCRIPT_VERSION="1.0.0"
|
|
47
|
+
|
|
48
|
+
# HELPERS
|
|
49
|
+
_validate_input() {
|
|
50
|
+
# ...
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# OPERATIONS
|
|
54
|
+
process_data() {
|
|
55
|
+
# ...
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# COMMANDS
|
|
59
|
+
cmd_run() {
|
|
60
|
+
_validate_input "$1"
|
|
61
|
+
process_data "$1"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# DISPATCH
|
|
65
|
+
case "${1:-}" in
|
|
66
|
+
run) shift; cmd_run "$@" ;;
|
|
67
|
+
*) printf 'Usage: script-name run\n' >&2; exit 1 ;;
|
|
68
|
+
esac
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Comment Style
|
|
72
|
+
|
|
73
|
+
Avoid em dashes or en dashes in comments.
|
|
74
|
+
|
|
75
|
+
Use a colon (`:`) to separate a name from its description.
|
|
76
|
+
|
|
77
|
+
Use a semicolon (`;`) to combine phrases into a single line when they are closely related.
|
|
78
|
+
|
|
79
|
+
Example comment styles
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# clobber.sh: A script to overwrite files; use with caution.
|
|
83
|
+
|
|
84
|
+
# Write config file (first run only; do not clobber user edits)
|
|
85
|
+
|
|
86
|
+
# path set by init --local; sync uses it instead of git pull
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Section Comments
|
|
90
|
+
|
|
91
|
+
Mark major sections with plain uppercase `#` labels. No decorative dashes or borders needed; the label is sufficient.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# CONSTANTS
|
|
95
|
+
# HELPERS
|
|
96
|
+
# COMMANDS
|
|
97
|
+
# DISPATCH
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
In longer scripts (several hundred lines or more), a horizontal rule comment may precede a major section label to add visual weight. Use these sparingly.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
# COMMANDS
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Naming Conventions
|
|
109
|
+
|
|
110
|
+
### Variables
|
|
111
|
+
|
|
112
|
+
<dl>
|
|
113
|
+
<dt class="hdlist1">Global variables and constants</dt>
|
|
114
|
+
<dd>
|
|
115
|
+
Use `SCREAMING_SNAKE_CASE`. Use `readonly` for constants.
|
|
116
|
+
|
|
117
|
+
- `readonly MAX_RETRIES=5`
|
|
118
|
+
|
|
119
|
+
- `APP_CONFIG_PATH=".env"`
|
|
120
|
+
</dd>
|
|
121
|
+
<dt class="hdlist1">Local variables</dt>
|
|
122
|
+
<dd>
|
|
123
|
+
Use `snake_case` and `local` declaration.
|
|
124
|
+
|
|
125
|
+
- `local user_name="$1"`
|
|
126
|
+
</dd>
|
|
127
|
+
</dl>
|
|
128
|
+
|
|
129
|
+
### Functions
|
|
130
|
+
|
|
131
|
+
<dl>
|
|
132
|
+
<dt class="hdlist1">Operation functions</dt>
|
|
133
|
+
<dd>
|
|
134
|
+
The substantive work of a script; what `cmd_` functions orchestrate, and what sourced libraries export as their callable API. Use unprefixed `snake_case`.
|
|
135
|
+
|
|
136
|
+
- `evaluate_system()`, `build_docker_image()`, `get_current_version()`
|
|
137
|
+
</dd>
|
|
138
|
+
<dt class="hdlist1">Helper functions</dt>
|
|
139
|
+
<dd>
|
|
140
|
+
Prefix internal utility functions with ``. This applies in both standalone scripts and sourced library files. In a sourced library, the `` prefix signals that these functions are implementation details and reduces the risk of collisions in the calling script’s namespace.
|
|
141
|
+
|
|
142
|
+
- `_bold()`, `_check_help()`, `_resolve_slug()`, `_check_project_root()`
|
|
143
|
+
</dd>
|
|
144
|
+
<dt class="hdlist1">Subcommand handlers</dt>
|
|
145
|
+
<dd>
|
|
146
|
+
Prefix functions that implement top-level subcommands with `cmd_`. The dispatch `case` at the bottom of the script maps argument strings to these functions unambiguously.
|
|
147
|
+
|
|
148
|
+
- `cmd_init()`, `cmd_run()`, `cmd_check()`
|
|
149
|
+
</dd>
|
|
150
|
+
</dl>
|
|
151
|
+
|
|
152
|
+
## Variables and Data
|
|
153
|
+
|
|
154
|
+
### Declaration and Scoping
|
|
155
|
+
|
|
156
|
+
Always use `local` to declare variables inside functions. This prevents polluting the global scope and avoids unintended side effects.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
_some_action() {
|
|
160
|
+
local file_path="$1" # Good: variable is local to the function
|
|
161
|
+
count=0 # Avoid: variable is global by default
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Quoting
|
|
166
|
+
|
|
167
|
+
Always quote variable expansions (`"$variable"`) and command substitutions (`"$(command)"`) to prevent issues with word splitting and unexpected filename expansion (globbing).
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Good: handles spaces and special characters in filenames
|
|
171
|
+
echo "$file_name"
|
|
172
|
+
touch "$new_file"
|
|
173
|
+
|
|
174
|
+
# Avoid: will fail if file_name contains spaces
|
|
175
|
+
echo $file_name
|
|
176
|
+
touch $new_file
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
> **NOTE:** Names of files created by DocOps Lab should never include spaces, but this habit is important for dealing with user input or external data. Always remember that many of our users come from Windows, where spaces in filenames are common.
|
|
180
|
+
|
|
181
|
+
### Arrays
|
|
182
|
+
|
|
183
|
+
Use standard indexed arrays for lists of items.
|
|
184
|
+
|
|
185
|
+
Use associative arrays (`declare -A`) for key-value pairs (i.e., maps).
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# Indexed array
|
|
189
|
+
local -a packages=("git" "curl" "jq")
|
|
190
|
+
echo "First package is: ${packages[0]}"
|
|
191
|
+
|
|
192
|
+
# Associative array
|
|
193
|
+
declare -A user_details
|
|
194
|
+
user_details["name"]="John Doe"
|
|
195
|
+
user_details["email"]="john.doe@example.com"
|
|
196
|
+
echo "User email: ${user_details["email"]}"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Functions
|
|
200
|
+
|
|
201
|
+
### Syntax
|
|
202
|
+
|
|
203
|
+
Use the `name() { }` syntax. Do not use the `function` keyword; it is redundant in Bash and creates visual inconsistency when mixed with bare definitions.
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Good
|
|
207
|
+
some_action() {
|
|
208
|
+
local arg="$1"
|
|
209
|
+
# ...
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# Avoid
|
|
213
|
+
function some_action() {
|
|
214
|
+
local arg="$1"
|
|
215
|
+
# ...
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Single-line form is acceptable for very short utility functions:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
_bold() { printf '\033[1m%s\033[0m' "$*"; }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Arguments
|
|
226
|
+
|
|
227
|
+
Access arguments using positional parameters (`$1`, `$2`, etc.). Use `"$@"` to forward all arguments.
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
_log_message() {
|
|
231
|
+
local level="$1"
|
|
232
|
+
local message="$2"
|
|
233
|
+
echo "[$level] $message"
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
_log_message "INFO" "Process complete."
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Returning Values
|
|
240
|
+
|
|
241
|
+
**To return a string or data** , use `echo` or `printf` and capture the output using command substitution.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
_get_user_home() {
|
|
245
|
+
local user="$1"
|
|
246
|
+
# ... logic to find home directory ...
|
|
247
|
+
echo "/home/$user" # Returns string via stdout
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**To return a status** , use `return` with a numeric code. `0` means success, and any non-zero value (`1-255`) indicates failure.
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
_check_file_exists() {
|
|
255
|
+
if [[-f "$1"]]; then
|
|
256
|
+
return 0 # Success
|
|
257
|
+
else
|
|
258
|
+
return 1 # Failure
|
|
259
|
+
fi
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Conditionals
|
|
264
|
+
|
|
265
|
+
Use `[[…]]` for conditional tests. It is more powerful, prevents many common errors, and is easier to use than the older `[…]` or `test` builtins.
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# Good
|
|
269
|
+
if [["$name" == "admin" && -f "$config_file"]]; then
|
|
270
|
+
# ...
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
# Avoid
|
|
274
|
+
if ["$name" = "admin" -a -f "$config_file"]; then
|
|
275
|
+
# ...
|
|
276
|
+
fi
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
For dispatching based on a command or option, `case` statements are often cleaner than long `if/elif/else` chains.
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
case "$command" in
|
|
283
|
+
build) cmd_build
|
|
284
|
+
;;
|
|
285
|
+
run) cmd_run
|
|
286
|
+
;;
|
|
287
|
+
*)
|
|
288
|
+
printf 'Error: unknown command: %s\n' "$command" >&2
|
|
289
|
+
exit 1
|
|
290
|
+
;;
|
|
291
|
+
esac
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Error Handling
|
|
295
|
+
|
|
296
|
+
Use `set -euo pipefail` at the top of every script.
|
|
297
|
+
|
|
298
|
+
<dl>
|
|
299
|
+
<dt class="hdlist1">`e`</dt>
|
|
300
|
+
<dd>
|
|
301
|
+
Exit immediately when any command returns a non-zero status.
|
|
302
|
+
</dd>
|
|
303
|
+
<dt class="hdlist1">`u`</dt>
|
|
304
|
+
<dd>
|
|
305
|
+
Treat unset variables as an error, catching silent bugs from empty references.
|
|
306
|
+
</dd>
|
|
307
|
+
<dt class="hdlist1">`o pipefail`</dt>
|
|
308
|
+
<dd>
|
|
309
|
+
Fail a pipeline if any command within it fails, not just the last one.
|
|
310
|
+
</dd>
|
|
311
|
+
</dl>
|
|
312
|
+
|
|
313
|
+
Print error messages to standard error (`stderr`) and exit with a non-zero status.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
#!/usr/bin/env bash
|
|
317
|
+
set -euo pipefail
|
|
318
|
+
|
|
319
|
+
printf 'Error: something went wrong.\n' >&2
|
|
320
|
+
exit 1
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
> **NOTE:** Some scripts warrant more selective error handling. A container entrypoint running as PID 1, or a script that sources untrusted config, may use `set -e` alone. Document any deviations and the reason for them.
|
|
324
|
+
|
|
325
|
+
### Cleanup Traps
|
|
326
|
+
|
|
327
|
+
For scripts that create temporary resources or modify system state, register a cleanup function with `trap` so those resources are removed whether the script exits normally, fails under `set -e`, or is interrupted.
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
_cleanup() {
|
|
331
|
+
[[-n "${tmp_file:-}"]] && rm -f "$tmp_file"
|
|
332
|
+
}
|
|
333
|
+
trap _cleanup EXIT INT TERM
|
|
334
|
+
|
|
335
|
+
tmp_file="$(mktemp)"
|
|
336
|
+
# ... work with $tmp_file ...
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
> **TIP:** The `EXIT` pseudo-signal fires on both normal exit and on `set -e` termination. Adding `INT` and `TERM` ensures cleanup even when the user presses Ctrl+C or the process is sent SIGTERM.
|
|
340
|
+
|
|
341
|
+
### Sourced Libraries
|
|
342
|
+
|
|
343
|
+
Do not place `set -euo pipefail` inside a sourced library file. The calling script owns the error mode. The library’s functions will execute under whatever error mode the caller established.
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
#!/usr/bin/env bash
|
|
347
|
+
# my-lib.sh; shared helpers sourced by build scripts.
|
|
348
|
+
# Do NOT add set -euo pipefail here.
|
|
349
|
+
|
|
350
|
+
_do_something() {
|
|
351
|
+
# ...
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
For file-level variables that a sourced library exports for its callers to read, ShellCheck will emit `SC2034` ("variable appears unused"). Suppress it inline on those specific declarations rather than disabling the check globally.
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
# shellcheck disable=SC2034 # exported; read by callers
|
|
359
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Intentional Word-splitting
|
|
363
|
+
|
|
364
|
+
When a variable genuinely needs to be word-split (ex: an options string passed to a command), suppress the ShellCheck warning inline on that specific line. Add a comment explaining the intent.
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# shellcheck disable=SC2086 # intentional: $docker_args may contain multiple flags
|
|
368
|
+
docker build ${docker_args} -t "${image}" .
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Output
|
|
372
|
+
|
|
373
|
+
Prefer `printf` over `echo` for all script output.
|
|
374
|
+
|
|
375
|
+
- `printf` is predictable, portable across Bash scripts, and supports format strings.
|
|
376
|
+
|
|
377
|
+
- `echo` behaviour varies across shells and platforms, particularly with `-e` and `-n`.
|
|
378
|
+
|
|
379
|
+
- Never use `echo -e`.
|
|
380
|
+
|
|
381
|
+
- Use `printf` with `\n`, or a heredoc.
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
# Good
|
|
385
|
+
printf 'Error: %s not found.\n' "$name" >&2
|
|
386
|
+
|
|
387
|
+
# Capture heredoc as a local var
|
|
388
|
+
read -r -d '' error_message <<'ERRMSG'
|
|
389
|
+
Error: Something went wrong.
|
|
390
|
+
Please check your configuration and try again.
|
|
391
|
+
ERRMSG
|
|
392
|
+
printf '%s\n' "$error_message"
|
|
393
|
+
|
|
394
|
+
# Avoid
|
|
395
|
+
echo -e "Error: $name not found.\n"
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
> **TIP:** Note the use of semantic heredoc delimiters (`ERRMSG`) instead of generic `EOF` or `HEREDOC`.
|
|
399
|
+
|
|
400
|
+
For output intended for the user (status ticks, warnings, separators), use the shared style helpers from the centrally maintained [`universals.sh`](https://github.com/DocOps/lab/blob/main/gems/docopslab-dev/assets/templates/universals.sh) rather than writing raw ANSI codes inline. See the `universal-style-helpers` tagged segment for the canonical set.
|
|
401
|
+
|
|
402
|
+
Universal style helpers common to all DocOps Lab scripts
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
_bold() { printf '\033[1m%s\033[0m' "$*"; }
|
|
406
|
+
_green() { printf '\033[32m%s\033[0m' "$*"; }
|
|
407
|
+
_yellow() { printf '\033[33m%s\033[0m' "$*"; }
|
|
408
|
+
_red() { printf '\033[31m%s\033[0m' "$*"; }
|
|
409
|
+
_tick() { printf '%s %s\n' "$(_green '✓')" "$*"; }
|
|
410
|
+
_warn() { printf '%s %s\n' "$(_yellow '⚠')" "$*"; }
|
|
411
|
+
_fail() { printf '%s %s\n' "$(_red '✗')" "$*"; }
|
|
412
|
+
_info() { printf ' %s\n' "$*"; }
|
|
413
|
+
_sep() { printf '%s\n' "────────────────────────────────────────────────"; }
|
|
414
|
+
_run_echo() { printf '\n%s %s\n\n' "$(_bold '▶')" "$(_bold "$*")"; }
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## Practices to Avoid
|
|
418
|
+
|
|
419
|
+
### Avoid Emojis in Output
|
|
420
|
+
|
|
421
|
+
Do not use emojis in script output. Use the symbol helpers from `universal-style-helpers` (`_tick`, `_warn`, `_fail`, `_info`) which provide consistent Unicode characters (`✓`, `⚠`, `✗`).
|
|
422
|
+
|
|
423
|
+
Let’s keep it classy.
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
# Good
|
|
427
|
+
_tick "Gem built: $gem_file"
|
|
428
|
+
_fail "Docker not found."
|
|
429
|
+
|
|
430
|
+
# Avoid
|
|
431
|
+
echo -e "\U2705 Gem built: $gem_file"
|
|
432
|
+
echo "❌ Docker not found."
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Avoid `eval`
|
|
436
|
+
|
|
437
|
+
The `eval` command can execute arbitrary code and poses a significant security risk if used with external or user-provided data.
|
|
438
|
+
|
|
439
|
+
It also makes code difficult to debug.
|
|
440
|
+
|
|
441
|
+
Avoid it whenever possible. Modern Bash versions provide safer alternatives like namerefs (`declare -n`) for indirect variable/array manipulation.
|
|
442
|
+
|
|
443
|
+
### Avoid Backticks
|
|
444
|
+
|
|
445
|
+
Use `$(...)` for command substitution instead of backticks (``...``). It is easier to read and can be nested.
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
# Good
|
|
449
|
+
current_dir="$(pwd)"
|
|
450
|
+
|
|
451
|
+
# Avoid
|
|
452
|
+
current_dir=`pwd`
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Avoid `which`
|
|
456
|
+
|
|
457
|
+
Use `command -v` to test whether a command is available on the `PATH`.`which` is an external binary whose behavior varies across systems and is not available everywhere.`command -v` is a Bash builtin and the POSIX-portable alternative.
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
# Good
|
|
461
|
+
if command -v docker &>/dev/null; then
|
|
462
|
+
# ...
|
|
463
|
+
fi
|
|
464
|
+
|
|
465
|
+
# Avoid
|
|
466
|
+
if which docker > /dev/null 2>&1; then
|
|
467
|
+
# ...
|
|
468
|
+
fi
|
|
469
|
+
```
|
|
470
|
+
|