Initial commit
This commit is contained in:
454
skills/shellcheck-configuration/SKILL.md
Normal file
454
skills/shellcheck-configuration/SKILL.md
Normal file
@@ -0,0 +1,454 @@
|
||||
---
|
||||
name: shellcheck-configuration
|
||||
description: Master ShellCheck static analysis configuration and usage for shell script quality. Use when setting up linting infrastructure, fixing code issues, or ensuring script portability.
|
||||
---
|
||||
|
||||
# ShellCheck Configuration and Static Analysis
|
||||
|
||||
Comprehensive guidance for configuring and using ShellCheck to improve shell script quality, catch common pitfalls, and enforce best practices through static code analysis.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Setting up linting for shell scripts in CI/CD pipelines
|
||||
- Analyzing existing shell scripts for issues
|
||||
- Understanding ShellCheck error codes and warnings
|
||||
- Configuring ShellCheck for specific project requirements
|
||||
- Integrating ShellCheck into development workflows
|
||||
- Suppressing false positives and configuring rule sets
|
||||
- Enforcing consistent code quality standards
|
||||
- Migrating scripts to meet quality gates
|
||||
|
||||
## ShellCheck Fundamentals
|
||||
|
||||
### What is ShellCheck?
|
||||
|
||||
ShellCheck is a static analysis tool that analyzes shell scripts and detects problematic patterns. It supports:
|
||||
- Bash, sh, dash, ksh, and other POSIX shells
|
||||
- Over 100 different warnings and errors
|
||||
- Configuration for target shell and flags
|
||||
- Integration with editors and CI/CD systems
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# macOS with Homebrew
|
||||
brew install shellcheck
|
||||
|
||||
# Ubuntu/Debian
|
||||
apt-get install shellcheck
|
||||
|
||||
# From source
|
||||
git clone https://github.com/koalaman/shellcheck.git
|
||||
cd shellcheck
|
||||
make build
|
||||
make install
|
||||
|
||||
# Verify installation
|
||||
shellcheck --version
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### .shellcheckrc (Project Level)
|
||||
|
||||
Create `.shellcheckrc` in your project root:
|
||||
|
||||
```
|
||||
# Specify target shell
|
||||
shell=bash
|
||||
|
||||
# Enable optional checks
|
||||
enable=avoid-nullary-conditions
|
||||
enable=require-variable-braces
|
||||
|
||||
# Disable specific warnings
|
||||
disable=SC1091
|
||||
disable=SC2086
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# Set default shell target
|
||||
export SHELLCHECK_SHELL=bash
|
||||
|
||||
# Enable strict mode
|
||||
export SHELLCHECK_STRICT=true
|
||||
|
||||
# Specify configuration file location
|
||||
export SHELLCHECK_CONFIG=~/.shellcheckrc
|
||||
```
|
||||
|
||||
## Common ShellCheck Error Codes
|
||||
|
||||
### SC1000-1099: Parser Errors
|
||||
```bash
|
||||
# SC1004: Backslash continuation not followed by newline
|
||||
echo hello\
|
||||
world # Error - needs line continuation
|
||||
|
||||
# SC1008: Invalid data for operator `=='
|
||||
if [[ $var = "value" ]]; then # Space before ==
|
||||
true
|
||||
fi
|
||||
```
|
||||
|
||||
### SC2000-2099: Shell Issues
|
||||
|
||||
```bash
|
||||
# SC2009: Consider using pgrep or pidof instead of grep|grep
|
||||
ps aux | grep -v grep | grep myprocess # Use pgrep instead
|
||||
|
||||
# SC2012: Use `ls` only for viewing. Use `find` for reliable output
|
||||
for file in $(ls -la) # Better: use find or globbing
|
||||
|
||||
# SC2015: Avoid using && and || instead of if-then-else
|
||||
[[ -f "$file" ]] && echo "found" || echo "not found" # Less clear
|
||||
|
||||
# SC2016: Expressions don't expand in single quotes
|
||||
echo '$VAR' # Literal $VAR, not variable expansion
|
||||
|
||||
# SC2026: This word is non-standard. Set POSIXLY_CORRECT
|
||||
# when using with scripts for other shells
|
||||
```
|
||||
|
||||
### SC2100-2199: Quoting Issues
|
||||
|
||||
```bash
|
||||
# SC2086: Double quote to prevent globbing and word splitting
|
||||
for i in $list; do # Should be: for i in $list or for i in "$list"
|
||||
echo "$i"
|
||||
done
|
||||
|
||||
# SC2115: Literal tilde in path not expanded. Use $HOME instead
|
||||
~/.bashrc # In strings, use "$HOME/.bashrc"
|
||||
|
||||
# SC2181: Check exit code directly with `if`, not indirectly in a list
|
||||
some_command
|
||||
if [ $? -eq 0 ]; then # Better: if some_command; then
|
||||
|
||||
# SC2206: Quote to prevent word splitting or set IFS
|
||||
array=( $items ) # Should use: array=( $items )
|
||||
```
|
||||
|
||||
### SC3000-3999: POSIX Compliance Issues
|
||||
|
||||
```bash
|
||||
# SC3010: In POSIX sh, use 'case' instead of 'cond && foo'
|
||||
[[ $var == "value" ]] && do_something # Not POSIX
|
||||
|
||||
# SC3043: In POSIX sh, use 'local' is undefined
|
||||
function my_func() {
|
||||
local var=value # Not POSIX in some shells
|
||||
}
|
||||
```
|
||||
|
||||
## Practical Configuration Examples
|
||||
|
||||
### Minimal Configuration (Strict POSIX)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Configure for maximum portability
|
||||
|
||||
shellcheck \
|
||||
--shell=sh \
|
||||
--external-sources \
|
||||
--check-sourced \
|
||||
script.sh
|
||||
```
|
||||
|
||||
### Development Configuration (Bash with Relaxed Rules)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Configure for Bash development
|
||||
|
||||
shellcheck \
|
||||
--shell=bash \
|
||||
--exclude=SC1091,SC2119 \
|
||||
--enable=all \
|
||||
script.sh
|
||||
```
|
||||
|
||||
### CI/CD Integration Configuration
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Analyze all shell scripts and fail on issues
|
||||
find . -type f -name "*.sh" | while read -r script; do
|
||||
echo "Checking: $script"
|
||||
shellcheck \
|
||||
--shell=bash \
|
||||
--format=gcc \
|
||||
--exclude=SC1091 \
|
||||
"$script" || exit 1
|
||||
done
|
||||
```
|
||||
|
||||
### .shellcheckrc for Project
|
||||
|
||||
```
|
||||
# Shell dialect to analyze against
|
||||
shell=bash
|
||||
|
||||
# Enable optional checks
|
||||
enable=avoid-nullary-conditions,require-variable-braces,check-unassigned-uppercase
|
||||
|
||||
# Disable specific warnings
|
||||
# SC1091: Not following sourced files (many false positives)
|
||||
disable=SC1091
|
||||
|
||||
# SC2119: Use function_name instead of function_name -- (arguments)
|
||||
disable=SC2119
|
||||
|
||||
# External files to source for context
|
||||
external-sources=true
|
||||
```
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### Pre-commit Hook Configuration
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# .git/hooks/pre-commit
|
||||
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Find all shell scripts changed in this commit
|
||||
git diff --cached --name-only | grep '\.sh$' | while read -r script; do
|
||||
echo "Linting: $script"
|
||||
|
||||
if ! shellcheck "$script"; then
|
||||
echo "ShellCheck failed on $script"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
### GitHub Actions Workflow
|
||||
|
||||
```yaml
|
||||
name: ShellCheck
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Run ShellCheck
|
||||
run: |
|
||||
sudo apt-get install shellcheck
|
||||
find . -type f -name "*.sh" -exec shellcheck {} \;
|
||||
```
|
||||
|
||||
### GitLab CI Pipeline
|
||||
|
||||
```yaml
|
||||
shellcheck:
|
||||
stage: lint
|
||||
image: koalaman/shellcheck-alpine
|
||||
script:
|
||||
- find . -type f -name "*.sh" -exec shellcheck {} \;
|
||||
allow_failure: false
|
||||
```
|
||||
|
||||
## Handling ShellCheck Violations
|
||||
|
||||
### Suppressing Specific Warnings
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Disable warning for entire line
|
||||
# shellcheck disable=SC2086
|
||||
for file in $(ls -la); do
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
# Disable for entire script
|
||||
# shellcheck disable=SC1091,SC2119
|
||||
|
||||
# Disable multiple warnings (format varies)
|
||||
command_that_fails() {
|
||||
# shellcheck disable=SC2015
|
||||
[ -f "$1" ] && echo "found" || echo "not found"
|
||||
}
|
||||
|
||||
# Disable specific check for source directive
|
||||
# shellcheck source=./helper.sh
|
||||
source helper.sh
|
||||
```
|
||||
|
||||
### Common Violations and Fixes
|
||||
|
||||
#### SC2086: Double quote to prevent word splitting
|
||||
|
||||
```bash
|
||||
# Problem
|
||||
for i in $list; do done
|
||||
|
||||
# Solution
|
||||
for i in $list; do done # If $list is already quoted, or
|
||||
for i in "${list[@]}"; do done # If list is an array
|
||||
```
|
||||
|
||||
#### SC2181: Check exit code directly
|
||||
|
||||
```bash
|
||||
# Problem
|
||||
some_command
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "success"
|
||||
fi
|
||||
|
||||
# Solution
|
||||
if some_command; then
|
||||
echo "success"
|
||||
fi
|
||||
```
|
||||
|
||||
#### SC2015: Use if-then instead of && ||
|
||||
|
||||
```bash
|
||||
# Problem
|
||||
[ -f "$file" ] && echo "exists" || echo "not found"
|
||||
|
||||
# Solution - clearer intent
|
||||
if [ -f "$file" ]; then
|
||||
echo "exists"
|
||||
else
|
||||
echo "not found"
|
||||
fi
|
||||
```
|
||||
|
||||
#### SC2016: Expressions don't expand in single quotes
|
||||
|
||||
```bash
|
||||
# Problem
|
||||
echo 'Variable value: $VAR'
|
||||
|
||||
# Solution
|
||||
echo "Variable value: $VAR"
|
||||
```
|
||||
|
||||
#### SC2009: Use pgrep instead of grep
|
||||
|
||||
```bash
|
||||
# Problem
|
||||
ps aux | grep -v grep | grep myprocess
|
||||
|
||||
# Solution
|
||||
pgrep -f myprocess
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Checking Multiple Files
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Sequential checking
|
||||
for script in *.sh; do
|
||||
shellcheck "$script"
|
||||
done
|
||||
|
||||
# Parallel checking (faster)
|
||||
find . -name "*.sh" -print0 | \
|
||||
xargs -0 -P 4 -n 1 shellcheck
|
||||
```
|
||||
|
||||
### Caching Results
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
CACHE_DIR=".shellcheck_cache"
|
||||
mkdir -p "$CACHE_DIR"
|
||||
|
||||
check_script() {
|
||||
local script="$1"
|
||||
local hash
|
||||
local cache_file
|
||||
|
||||
hash=$(sha256sum "$script" | cut -d' ' -f1)
|
||||
cache_file="$CACHE_DIR/$hash"
|
||||
|
||||
if [[ ! -f "$cache_file" ]]; then
|
||||
if shellcheck "$script" > "$cache_file" 2>&1; then
|
||||
touch "$cache_file.ok"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
[[ -f "$cache_file.ok" ]]
|
||||
}
|
||||
|
||||
find . -name "*.sh" | while read -r script; do
|
||||
check_script "$script" || exit 1
|
||||
done
|
||||
```
|
||||
|
||||
## Output Formats
|
||||
|
||||
### Default Format
|
||||
|
||||
```bash
|
||||
shellcheck script.sh
|
||||
|
||||
# Output:
|
||||
# script.sh:1:3: warning: foo is referenced but not assigned. [SC2154]
|
||||
```
|
||||
|
||||
### GCC Format (for CI/CD)
|
||||
|
||||
```bash
|
||||
shellcheck --format=gcc script.sh
|
||||
|
||||
# Output:
|
||||
# script.sh:1:3: warning: foo is referenced but not assigned.
|
||||
```
|
||||
|
||||
### JSON Format (for parsing)
|
||||
|
||||
```bash
|
||||
shellcheck --format=json script.sh
|
||||
|
||||
# Output:
|
||||
# [{"file": "script.sh", "line": 1, "column": 3, "level": "warning", "code": 2154, "message": "..."}]
|
||||
```
|
||||
|
||||
### Quiet Format
|
||||
|
||||
```bash
|
||||
shellcheck --format=quiet script.sh
|
||||
|
||||
# Returns non-zero if issues found, no output otherwise
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Run ShellCheck in CI/CD** - Catch issues before merging
|
||||
2. **Configure for your target shell** - Don't analyze bash as sh
|
||||
3. **Document exclusions** - Explain why violations are suppressed
|
||||
4. **Address violations** - Don't just disable warnings
|
||||
5. **Enable strict mode** - Use `--enable=all` with careful exclusions
|
||||
6. **Update regularly** - Keep ShellCheck current for new checks
|
||||
7. **Use pre-commit hooks** - Catch issues locally before pushing
|
||||
8. **Integrate with editors** - Get real-time feedback during development
|
||||
|
||||
## Resources
|
||||
|
||||
- **ShellCheck GitHub**: https://github.com/koalaman/shellcheck
|
||||
- **ShellCheck Wiki**: https://www.shellcheck.net/wiki/
|
||||
- **Error Code Reference**: https://www.shellcheck.net/
|
||||
Reference in New Issue
Block a user