shell-script-hardening-patterns

Harden Bash automation scripts with TDD-first static and behavioral checks, safe Python invocation via uv, locking, persistent state, and review-driven correction loops.

5 stars

Best use case

shell-script-hardening-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Harden Bash automation scripts with TDD-first static and behavioral checks, safe Python invocation via uv, locking, persistent state, and review-driven correction loops.

Teams using shell-script-hardening-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/shell-script-hardening-patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/vamseeachanta/workspace-hub/main/.agents/skills/development/shell-script-hardening-patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/shell-script-hardening-patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How shell-script-hardening-patterns Compares

Feature / Agentshell-script-hardening-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Harden Bash automation scripts with TDD-first static and behavioral checks, safe Python invocation via uv, locking, persistent state, and review-driven correction loops.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Shell Script Hardening Patterns

Use when fixing or extending repo automation scripts (`*.sh`) that:
- call Python from Bash
- run as cron/watcher jobs
- mutate shared state
- interact with git or GitHub CLI
- are prone to silent failures or duplicate processing

## Core lessons

1. Write failing tests first, even for shell scripts.
2. For shell-script refactors, use a mix of:
   - static tests on script text for banned patterns
   - behavioral tests on companion Python logic
   - direct `bash -n` syntax checks
   - direct dry-run/manual command execution
3. After implementation, run an adversarial review. Expect at least one concurrency/state bug to surface.
4. If review finds a shared-state race, patch it immediately and re-run tests.

## Pattern 1: Safe Python invocation from Bash

Avoid:
- `python3 -c "...${user_or_path}..."`
- `uv run ... python -c "...${shell_var}..."`

Prefer:

```bash
uv run --no-project python - "$ARG1" "$ARG2" <<'PY'
import sys
from pathlib import Path

arg1 = sys.argv[1]
arg2 = sys.argv[2]
print(arg1, arg2)
PY
```

Rules:
- Pass dynamic values as CLI args, not embedded into Python source.
- In this workspace, use `uv run` rather than bare `python3`.
- Use heredoc Python for anything more than a trivial constant expression.

## Pattern 2: Schema alignment for shell + Python validator pairs

If a shell script consumes the same manifest/config as a Python validator:
- make the Python validator the pre-flight source of truth
- only keep minimal shell parsing for iteration/execution after validation
- add a bypass flag only when explicitly needed (`--skip-validation`)
- update the example manifest at the same time as the parser/schema

Checklist:
- same field names everywhere
- same required/optional semantics
- example file matches current schema
- tests cover duplicate names, empty lists, and invalid schema

## Pattern 3: Watcher locking for cron/background loops

For scripts like `watch-results.sh`:
- create a dedicated state dir
- use a single lock file with non-blocking `flock`
- exit gracefully if another instance is already running
- acquire the lock before mutating shared state

Template:

```bash
STATE_DIR="${REPO_ROOT}/queue/.watcher-state"
LOCK_FILE="${STATE_DIR}/watch-results.lock"
mkdir -p "${STATE_DIR}"

exec 9>"${LOCK_FILE}"
if ! flock -n 9; then
  echo "another watcher is already running; exiting gracefully"
  exit 0
fi
```

Important pitfall:
- Do not initialize/reset shared counters before lock acquisition.
- A concurrent no-op invocation can otherwise wipe active state.

## Pattern 4: Surface git pull failures instead of masking them

Avoid:

```bash
git pull origin main 2>/dev/null || true
```

Prefer a sync helper that:
- captures stdout/stderr
- logs the failure with timestamp
- increments a persistent failure counter
- resets the counter on success
- exits non-zero after `N` consecutive failures

Recommended state files:
- `git-pull-failures.count`
- `git-pull-failures.log`

Expose the count in the health/reporting script so operators can see drift even if the watcher is not currently running.

## Pattern 5: Health script reporting

If a health script summarizes queue/cron state:
- include operational counters like `git_pull_failures`
- emit JSON mode for machine consumption
- keep human-readable mode for manual debugging
- use `uv run --no-project python -` rather than `python3 -c` for JSON serialization or YAML extraction

## Pattern 6: GitHub issue idempotency in shell scripts

For automation that opens issues:
- check `gh auth status` before mutation
- write auth outcome into the JSON summary/log artifact
- search for an existing open issue with the target label before creating a new one
- reuse/update/comment on the existing issue instead of creating duplicates

Important pitfall:
- If unauthenticated failure is possible, write the JSON summary before exiting non-zero so the failure is still observable in logs.

## Test strategy that worked well

### Static tests
Read script text and assert:
- banned strings are absent (`python3 -c`, `git pull ... || true`)
- required mechanisms are present (`flock`, `gh auth status`, `gh issue list`)
- important ordering constraints hold (`acquire_lock` occurs before state reset)

### Behavioral tests
Use companion Python tests to validate:
- schema behavior
- duplicate detection
- empty-manifest rejection
- queue health aggregation logic

### Direct execution
Always run all of:
- `bash -n path/to/script.sh`
- targeted `uv run pytest ...`
- one real dry-run or `--once` invocation if the script supports it

## Review loop

After the first green pass:
1. Request a focused review on correctness + race conditions.
2. If review finds a race or state-ordering bug, add a new failing test first.
3. Patch immediately.
4. Re-run tests and re-review.

This was especially valuable for catching a subtle bug where a concurrent watcher invocation reset a failure counter before lock acquisition.

## Commit slicing

For multi-issue shell hardening, group commits by closely related fixes:
- schema + validation integration
- shell-injection hardening
- watcher locking + failure surfacing
- GitHub automation idempotency

## Commands to remember

```bash
bash -n scripts/foo.sh
uv run pytest tests/path/test_script_behavior.py -q
bash scripts/foo.sh --dry-run
bash scripts/foo.sh --once
```

## When this skill is a good fit

Use it when you see any of these symptoms:
- shell script embeds user/path values inside Python code strings
- cron/watch scripts silently swallow git failures
- duplicate processing from concurrent script runs
- automation keeps opening duplicate GitHub issues
- schema defined one way in Python and another way in Bash

Related Skills

blender-worktree-test-hardening

5
from vamseeachanta/workspace-hub

Recover and harden digitalmodel Blender automation work in isolated worktrees when uv/editable dependency paths break and local machines lack a Blender executable.

taxact-browser-automation-patterns

5
from vamseeachanta/workspace-hub

Patterns for automating TaxAct Business online (Ionic SPA) via Chrome browser MCP tools — field interaction, navigation, shadow DOM handling

post-smoke-ci-plan-boundary-hardening

5
from vamseeachanta/workspace-hub

Harden a CI follow-up plan after an initial smoke/unblock issue lands. Use for post-smoke red workflows where later gates surface broad lint/type debt and the plan must stay bounded, evidence-backed, and review-honest.

iterative-plan-hardening-with-adversarial-waves

5
from vamseeachanta/workspace-hub

Tighten a draft plan through repeated Codex/Gemini adversarial review waves without prematurely advancing it to plan-review.

github-actions-trigger-and-shell-gotchas

5
from vamseeachanta/workspace-hub

Prevent false verification gaps in GitHub Actions by checking push path filters, shell compatibility, and shared CI environment failures before concluding a workflow fix worked or failed.

documentation-contract-plan-hardening

5
from vamseeachanta/workspace-hub

Harden a documentation/contract plan before adversarial review by mapping every issue-scope requirement to independent acceptance criteria and tests, especially for routing/indexing contracts.

canonical-doc-contract-plan-hardening

5
from vamseeachanta/workspace-hub

Harden a documentation or mission-contract plan so adversarial reviewers cannot reject it for vague validator contracts, wrong artifact authority, or semantic drift gaps.

bounded-post-smoke-ci-hardening-plan

5
from vamseeachanta/workspace-hub

Plan post-smoke CI hardening as a bounded blocker-removal tranche instead of overpromising full green. Includes issue-body alignment, follow-up split rules, honest review bookkeeping, and reliable local validation commands.

plan-review-adversarial-hardening

5
from vamseeachanta/workspace-hub

Class-level workflow for plan-review hardening, adversarial review reruns, artifact grounding, approval hygiene, and prompt/path drift recovery.

github-interaction-limits-hardening

5
from vamseeachanta/workspace-hub

Lock down public GitHub repositories so only collaborators can interact with issues, PRs, comments, discussions, and other user-generated surfaces using repository interaction limits; includes expiry verification and renewal handling.

webhook-subscriptions

5
from vamseeachanta/workspace-hub

Create and manage webhook subscriptions for event-driven agent activation. Use when the user wants external services to trigger agent runs automatically.

shell-tdd

5
from vamseeachanta/workspace-hub

Shell-based TDD test harness patterns — pass/fail counters, common assertions, set -e gotchas, and exit code conventions for bash test scripts