442 lines
9.2 KiB
Markdown
442 lines
9.2 KiB
Markdown
---
|
|
name: shellcheck-cicd-2025
|
|
description: ShellCheck validation as non-negotiable 2025 workflow practice
|
|
---
|
|
|
|
## 🚨 CRITICAL GUIDELINES
|
|
|
|
### Windows File Path Requirements
|
|
|
|
**MANDATORY: Always Use Backslashes on Windows for File Paths**
|
|
|
|
When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`).
|
|
|
|
**Examples:**
|
|
- ❌ WRONG: `D:/repos/project/file.tsx`
|
|
- ✅ CORRECT: `D:\repos\project\file.tsx`
|
|
|
|
This applies to:
|
|
- Edit tool file_path parameter
|
|
- Write tool file_path parameter
|
|
- All file operations on Windows systems
|
|
|
|
|
|
### Documentation Guidelines
|
|
|
|
**NEVER create new documentation files unless explicitly requested by the user.**
|
|
|
|
- **Priority**: Update existing README.md files rather than creating new documentation
|
|
- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise
|
|
- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone
|
|
- **User preference**: Only create additional .md files when user specifically asks for documentation
|
|
|
|
|
|
---
|
|
|
|
# ShellCheck CI/CD Integration (2025)
|
|
|
|
## ShellCheck: Non-Negotiable in 2025
|
|
|
|
ShellCheck is now considered **mandatory** in modern bash workflows (2025 best practices):
|
|
|
|
### Latest Version: v0.11.0 (August 2025)
|
|
|
|
**What's New:**
|
|
- Full Bash 5.3 support (`${| cmd; }` and `source -p`)
|
|
- **New warnings**: SC2327/SC2328 (capture group issues)
|
|
- **POSIX.1-2024 compliance**: SC3013 removed (-ot/-nt/-ef now POSIX standard)
|
|
- Enhanced static analysis capabilities
|
|
- Improved performance and accuracy
|
|
|
|
### Why Mandatory?
|
|
|
|
- Catches subtle bugs before production
|
|
- Prevents common security vulnerabilities
|
|
- Enforces consistent code quality
|
|
- Required by most DevOps teams
|
|
- Standard in enterprise environments
|
|
- Supports latest POSIX.1-2024 standard
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
# Ubuntu/Debian
|
|
apt-get install shellcheck
|
|
|
|
# macOS
|
|
brew install shellcheck
|
|
|
|
# Alpine (Docker)
|
|
apk add shellcheck
|
|
|
|
# Windows (WSL/Git Bash)
|
|
choco install shellcheck
|
|
|
|
# Or download binary
|
|
wget https://github.com/koalaman/shellcheck/releases/latest/download/shellcheck-stable.linux.x86_64.tar.xz
|
|
tar -xf shellcheck-stable.linux.x86_64.tar.xz
|
|
sudo cp shellcheck-stable/shellcheck /usr/local/bin/
|
|
```
|
|
|
|
## GitHub Actions Integration
|
|
|
|
### Mandatory Pre-Merge Check
|
|
|
|
```yaml
|
|
# .github/workflows/shellcheck.yml
|
|
name: ShellCheck
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- '**.sh'
|
|
- '**Dockerfile'
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
shellcheck:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Run ShellCheck
|
|
uses: ludeeus/action-shellcheck@master
|
|
with:
|
|
severity: warning
|
|
format: gcc # or: tty, json, checkstyle
|
|
scandir: './scripts'
|
|
# Fail on any issues
|
|
ignore_paths: 'node_modules'
|
|
|
|
# Block merge on failures
|
|
- name: Annotate PR
|
|
if: failure()
|
|
uses: actions/github-script@v6
|
|
with:
|
|
script: |
|
|
github.rest.issues.createComment({
|
|
issue_number: context.issue.number,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
body: '⛔ ShellCheck validation failed. Fix issues before merging.'
|
|
})
|
|
```
|
|
|
|
## Azure DevOps Integration
|
|
|
|
```yaml
|
|
# azure-pipelines.yml
|
|
trigger:
|
|
- main
|
|
|
|
pr:
|
|
- main
|
|
|
|
stages:
|
|
- stage: Validate
|
|
jobs:
|
|
- job: ShellCheck
|
|
pool:
|
|
vmImage: 'ubuntu-24.04'
|
|
|
|
steps:
|
|
- script: |
|
|
sudo apt-get install -y shellcheck
|
|
displayName: 'Install ShellCheck'
|
|
|
|
- script: |
|
|
find . -name "*.sh" -type f | xargs shellcheck --format=gcc --severity=warning
|
|
displayName: 'Run ShellCheck'
|
|
failOnStderr: true
|
|
|
|
- task: PublishTestResults@2
|
|
condition: always()
|
|
inputs:
|
|
testResultsFormat: 'JUnit'
|
|
testResultsFiles: '**/shellcheck-results.xml'
|
|
failTaskOnFailedTests: true
|
|
```
|
|
|
|
## Git Hooks (Pre-Commit)
|
|
|
|
```bash
|
|
# .git/hooks/pre-commit
|
|
#!/usr/bin/env bash
|
|
set -o errexit
|
|
set -o nounset
|
|
set -o pipefail
|
|
|
|
# Find all staged .sh files
|
|
mapfile -t STAGED_SH < <(git diff --cached --name-only --diff-filter=ACMR | grep '\.sh$' || true)
|
|
|
|
if [ ${#STAGED_SH[@]} -eq 0 ]; then
|
|
exit 0
|
|
fi
|
|
|
|
echo "Running ShellCheck on staged files..."
|
|
|
|
# Run ShellCheck
|
|
shellcheck --format=gcc --severity=warning "${STAGED_SH[@]}"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "⛔ ShellCheck failed. Fix issues before committing."
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ ShellCheck passed"
|
|
exit 0
|
|
```
|
|
|
|
**Install Pre-Commit Hook:**
|
|
```bash
|
|
chmod +x .git/hooks/pre-commit
|
|
|
|
# Or use pre-commit framework
|
|
# .pre-commit-config.yaml
|
|
repos:
|
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
|
rev: v0.11.0.0
|
|
hooks:
|
|
- id: shellcheck
|
|
args: ['--severity=warning']
|
|
|
|
# Install
|
|
pip install pre-commit
|
|
pre-commit install
|
|
```
|
|
|
|
## VS Code Integration
|
|
|
|
```json
|
|
// .vscode/settings.json
|
|
{
|
|
"shellcheck.enable": true,
|
|
"shellcheck.run": "onType",
|
|
"shellcheck.executablePath": "/usr/local/bin/shellcheck",
|
|
"shellcheck.exclude": ["SC1090", "SC1091"], // Optional excludes
|
|
"shellcheck.customArgs": [
|
|
"-x", // Follow source files
|
|
"--severity=warning"
|
|
]
|
|
}
|
|
```
|
|
|
|
## Docker Build Integration
|
|
|
|
```dockerfile
|
|
# Dockerfile with ShellCheck validation
|
|
FROM alpine:3.19 AS builder
|
|
|
|
# Install ShellCheck
|
|
RUN apk add --no-cache shellcheck bash
|
|
|
|
# Copy scripts
|
|
COPY scripts/ /scripts/
|
|
|
|
# Validate all scripts before continuing
|
|
RUN find /scripts -name "*.sh" -type f -exec shellcheck --severity=warning {} +
|
|
|
|
# Final stage
|
|
FROM alpine:3.19
|
|
COPY --from=builder /scripts/ /scripts/
|
|
RUN chmod +x /scripts/*.sh
|
|
|
|
ENTRYPOINT ["/scripts/entrypoint.sh"]
|
|
```
|
|
|
|
## Common ShellCheck Rules (2025)
|
|
|
|
### New in v0.11.0: SC2327/SC2328 - Capture Groups
|
|
|
|
```bash
|
|
# ❌ Bad - Capture groups may not work as expected
|
|
if [[ "$string" =~ ([0-9]+)\.([0-9]+) ]]; then
|
|
echo "$1" # Wrong: $1 is script arg, not capture group
|
|
fi
|
|
|
|
# ✅ Good - Use BASH_REMATCH array
|
|
if [[ "$string" =~ ([0-9]+)\.([0-9]+) ]]; then
|
|
echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
|
|
fi
|
|
```
|
|
|
|
### SC2294: eval Negates Array Benefits (New)
|
|
|
|
```bash
|
|
# ❌ Bad - eval defeats array safety
|
|
eval "command ${array[@]}"
|
|
|
|
# ✅ Good - Direct array usage
|
|
command "${array[@]}"
|
|
```
|
|
|
|
### SC2295: Quote Expansions Inside ${}
|
|
|
|
```bash
|
|
# ❌ Bad
|
|
echo "${var-$default}" # $default not quoted
|
|
|
|
# ✅ Good
|
|
echo "${var-"$default"}"
|
|
```
|
|
|
|
### SC2086: Quote Variables
|
|
|
|
```bash
|
|
# ❌ Bad
|
|
file=$1
|
|
cat $file # Fails if filename has spaces
|
|
|
|
# ✅ Good
|
|
file=$1
|
|
cat "$file"
|
|
```
|
|
|
|
### SC2046: Quote Command Substitution
|
|
|
|
```bash
|
|
# ❌ Bad
|
|
for file in $(find . -name "*.txt"); do
|
|
echo $file
|
|
done
|
|
|
|
# ✅ Good
|
|
find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do
|
|
echo "$file"
|
|
done
|
|
```
|
|
|
|
### SC2155: Separate Declaration and Assignment
|
|
|
|
```bash
|
|
# ❌ Bad
|
|
local result=$(command) # Hides command exit code
|
|
|
|
# ✅ Good
|
|
local result
|
|
result=$(command)
|
|
```
|
|
|
|
### SC2164: Use cd || exit
|
|
|
|
```bash
|
|
# ❌ Bad
|
|
cd /some/directory
|
|
./script.sh # Runs in wrong dir if cd fails
|
|
|
|
# ✅ Good
|
|
cd /some/directory || exit 1
|
|
./script.sh
|
|
```
|
|
|
|
## Google Shell Style Guide (50-Line Limit)
|
|
|
|
2025 recommendation: Keep scripts under 50 lines:
|
|
|
|
```bash
|
|
# ❌ Bad: 500-line monolithic script
|
|
#!/usr/bin/env bash
|
|
# ... 500 lines of code ...
|
|
|
|
# ✅ Good: Modular scripts < 50 lines each
|
|
|
|
# lib/logging.sh (20 lines)
|
|
log_info() { echo "[INFO] $*"; }
|
|
log_error() { echo "[ERROR] $*" >&2; }
|
|
|
|
# lib/validation.sh (30 lines)
|
|
validate_input() { ... }
|
|
check_dependencies() { ... }
|
|
|
|
# main.sh (40 lines)
|
|
source "$(dirname "$0")/lib/logging.sh"
|
|
source "$(dirname "$0")/lib/validation.sh"
|
|
|
|
main() {
|
|
validate_input "$@"
|
|
check_dependencies
|
|
# ... core logic ...
|
|
}
|
|
|
|
main "$@"
|
|
```
|
|
|
|
## Enforce in CI/CD
|
|
|
|
### Fail Build on Issues
|
|
|
|
```yaml
|
|
# Strict enforcement
|
|
- name: ShellCheck (Strict)
|
|
run: |
|
|
shellcheck --severity=warning scripts/*.sh
|
|
# Exit code 1 fails the build
|
|
|
|
# Advisory only (warnings but don't fail)
|
|
- name: ShellCheck (Advisory)
|
|
run: |
|
|
shellcheck --severity=warning scripts/*.sh || true
|
|
# Logs warnings but doesn't fail
|
|
```
|
|
|
|
### Generate Reports
|
|
|
|
```bash
|
|
# JSON format for parsing
|
|
shellcheck --format=json scripts/*.sh > shellcheck-report.json
|
|
|
|
# GitHub annotations format
|
|
shellcheck --format=gcc scripts/*.sh
|
|
|
|
# Human-readable
|
|
shellcheck --format=tty scripts/*.sh
|
|
```
|
|
|
|
## Modern Error Handling Trio (2025)
|
|
|
|
Always use with ShellCheck validation:
|
|
|
|
```bash
|
|
#!/usr/bin/env bash
|
|
|
|
# Modern error handling (non-negotiable in 2025)
|
|
set -o errexit # Exit on command failure
|
|
set -o nounset # Exit on undefined variable
|
|
set -o pipefail # Exit on pipe failure
|
|
|
|
# ShellCheck approved
|
|
main() {
|
|
local config_file="${1:?Config file required}"
|
|
|
|
if [[ ! -f "$config_file" ]]; then
|
|
echo "Error: Config file not found: $config_file" >&2
|
|
return 1
|
|
fi
|
|
|
|
# Safe command execution
|
|
local result
|
|
result=$(process_config "$config_file")
|
|
|
|
echo "$result"
|
|
}
|
|
|
|
main "$@"
|
|
```
|
|
|
|
## Best Practices (2025)
|
|
|
|
1. **Run ShellCheck in CI/CD (mandatory)**
|
|
2. **Use pre-commit hooks** to catch issues early
|
|
3. **Keep scripts under 50 lines** (Google Style Guide)
|
|
4. **Use modern error handling trio** (errexit, nounset, pipefail)
|
|
5. **Fix all warnings** before merging
|
|
6. **Document any disabled rules** with reasoning
|
|
7. **Integrate with IDE** for real-time feedback
|
|
|
|
## Resources
|
|
|
|
- [ShellCheck](https://www.shellcheck.net)
|
|
- [Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html)
|
|
- [ShellCheck GitHub Action](https://github.com/ludeeus/action-shellcheck)
|