Generic Shell Script Template

January 3, 2026 · View on GitHub

License: MIT CI Contributions welcome

A modern, cross-platform template for Bash and PowerShell script projects with comprehensive tooling, testing, linting, and CI/CD built-in.

Quick Start

Bootstrap a new project:

# Bootstrap in current directory
uvx --from git+https://github.com/templ-project/generic.git bootstrap .

# Bootstrap in specific directory
uvx --from git+https://github.com/templ-project/generic.git bootstrap ./my-project

# Bootstrap with custom project name
uvx --from git+https://github.com/templ-project/generic.git bootstrap --project-name my-awesome-project ./target-dir

After bootstrapping:

cd my-project
git init
task deps:sync        # Install all dependencies (mise, npm, uv)
git add .
git commit -m "Initial commit"

That's it! You now have a fully configured shell script project.

What's Included

FeatureToolDescription
LanguagesBash + PowerShellCross-platform shell scripting
Task RunnerTaskfileModern build automation
Tool ManagementmiseIsolated development environment
Shell LintingShellCheckBash/sh static analysis with auto-fix
PowerShell LintingPSScriptAnalyzerPowerShell best practices
Python LintingPylint + RuffFor helper scripts
Code FormattingPrettierJSON, YAML, Markdown formatting
Bash TestingBats-coreBash Automated Testing System
PowerShell TestingPesterPowerShell testing framework
Pre-commit HooksHusky + lint-stagedAutomatic validation
Duplicate DetectionjscpdCopy-paste detector
DocumentationMkDocsMaterial theme docs
CI/CDGitHub ActionsMulti-platform testing & releases

Common Commands

# === Development ===
task run                 # Run the CLI script
task build               # Build/validate project

# === Code Formatting ===
task format              # Format all code (Prettier, Ruff)
task format:check        # Check formatting without fixing

# === Linting ===
task lint                # Lint all code (ESLint, Pylint, ShellCheck, PSScriptAnalyzer)
task lint:check          # Check all without fixing
task lint:shlint         # Lint bash scripts only
task lint:pwshlint       # Lint PowerShell scripts only

# === Testing ===
task test                # Run all tests (Bats + Pester)
task test:bats           # Run Bash tests only
task test:pester         # Run PowerShell tests only

# === Code Quality ===
task duplicate-check     # Check for duplicate code

# === Documentation ===
task docs                # Build documentation
task docs:serve          # Serve documentation locally

# === Full Validation ===
task validate            # Run complete CI pipeline locally

# === Dependencies ===
task deps:sync           # Install all dependencies
task deps:refresh        # Update all dependencies
task deps:clean          # Remove all dependencies

Requirements

  • mise - Tool version management (installs everything else)
  • Task - Task runner (can be installed via mise or standalone)

Automatically installed via mise:

  • Node.js 22 (for ESLint, Prettier, jscpd)
  • Python 3.11+ (for linting scripts, docs)
  • ShellCheck (bash linting)
  • PowerShell Core (cross-platform PowerShell)

Setup Development Environment

# Install mise (if not already installed)
# Linux/macOS:
curl https://mise.run | sh

# Windows (PowerShell):
winget install jdx.mise
# or: choco install mise

# Install Task runner
# https://taskfile.dev/installation/

# Clone and setup
git clone https://github.com/templ-project/generic.git my-project
cd my-project

# Install all dependencies
task deps:sync

# Verify setup
task validate

Project Structure

├── .github/
│   └── workflows/        # CI/CD pipelines
│       ├── ci.yml        # Main CI orchestrator
│       ├── ci.quality.yml# Lint, test, build jobs
│       ├── ci.release.yml# GitHub release publishing
│       └── ci.version.yml# Semantic versioning (Bumpalicious)
├── .scripts/             # Build/lint helper scripts
│   ├── shlint.py         # ShellCheck wrapper with auto-fix
│   ├── pwshlint.py       # PSScriptAnalyzer wrapper
│   ├── run-pester.ps1    # Pester test runner
│   └── fix-mise-pwsh.*   # mise PowerShell installation fix
├── .taskfiles/           # Shared Taskfile modules
├── src/                  # Source scripts
│   ├── cli.sh            # Bash CLI entrypoint
│   ├── cli.ps1           # PowerShell CLI entrypoint
│   ├── greeter.sh        # Bash library module
│   └── greeter.ps1       # PowerShell library module
├── test/                 # Test files
│   ├── greeter.test.bats # Bash tests (Bats)
│   └── greeter.Tests.ps1 # PowerShell tests (Pester)
├── docs/                 # Documentation source
├── _uvx_install/         # Bootstrap script (for uvx)
├── Taskfile.yml          # Task definitions
├── .mise.toml            # Tool versions & hooks
├── package.json          # Node.js dev dependencies
├── pyproject.toml        # Python config & dependencies
└── VERSION               # Project version (semver)

Testing

Bash Tests (Bats)

Tests are in test/*.bats:

# test/greeter.test.bats
@test "hello returns greeting" {
  source src/greeter.sh
  result="$(hello "World")"
  [ "$result" = "Hello, World!" ]
}

Run with: task test:bats

PowerShell Tests (Pester)

Tests are in test/*.Tests.ps1:

# test/greeter.Tests.ps1
Describe "Hello function" {
    It "Returns greeting" {
        . src/greeter.ps1
        Hello -Name "World" | Should -Be "Hello, World!"
    }
}

Run with: task test:pester

Code Quality

Pre-commit Hooks

Automatic validation via Husky + lint-staged:

File TypeTools Run
*.sh, *.batsShellCheck
*.ps1PSScriptAnalyzer
*.pyRuff format, Pylint
*.json, *.yml, *.mdPrettier, ESLint

Configure in:

  • .husky/pre-commit - Hook script
  • .lintstagedrc.yml - File patterns and commands

Configuration

FilePurpose
.mise.tomlTool versions (Node, Python, ShellCheck, PowerShell)
Taskfile.ymlTask definitions
.shellcheckrcShellCheck rules
.PSScriptAnalyzerSettings.psd1PSScriptAnalyzer rules
pyproject.tomlPython tools (Pylint, Ruff, Black)
eslint.config.mjsESLint config (uses @templ-project/eslint)
prettier.config.mjsPrettier config (uses @templ-project/prettier)
.jscpd.jsonDuplicate detection settings

Using as a Library

Bash

Source the library modules in your scripts:

#!/usr/bin/env bash

# Source the greeter module
source /path/to/src/greeter.sh

# Use the functions
message=$(hello "World")
echo "$message"  # "Hello, World!"

PowerShell

Dot-source the library modules in your scripts:

#!/usr/bin/env pwsh

# Dot-source the greeter module
. /path/to/src/greeter.ps1

# Use the functions
$message = Hello -Name "World"
Write-Output $message  # "Hello, World!"

CI/CD Pipeline

The GitHub Actions pipeline runs on Linux, macOS, and Windows:

WorkflowTriggerJobs
ci.ymlPush/PR to main/developMatrix orchestrator
ci.quality.ymlCalled by ci.ymllint, test, build, duplicate-check
ci.version.ymlPush to mainSemantic version bump (Bumpalicious)
ci.release.ymlAfter version bumpCreate GitHub release with archives

Release artifacts:

  • {project}-{version}.tar.gz - Unix archive
  • {project}-{version}.zip - Windows archive

Both contain the src/ scripts, README.md, and LICENSE.

License

MIT © Templ Project

Support