Capstone: Build a Production-Grade Release Orchestrator
The capstone combines the entire course into one coherent workflow: explicit inputs, safe temporary state, dependency checks, artifact verification, dry-run, observability, durable release metadata, and predictable failure behavior.
Learning objectives
By the end of this lesson
- Build a complete release lifecycle.
- Combine validation, preflight, staging, and verification.
- Separate stdout results from diagnostics.
- Record current and previous releases.
- Identify remaining hardening steps for production.
1. Capstone: a production-grade release orchestrator
The final project combines the course: argument validation, dependency preflight, private temporary state, artifact staging, checksums, dry-run, structured logs, explicit statuses, durable current/previous release state, and cleanup.
flowchart TD
A["parse and validate"] --> P["preflight"]
P --> B["build artifact"]
B --> V["verify"]
V --> D{"dry run?"}
D -->|"yes"| R["report plan"]
D -->|"no"| U["publish release"]
U --> H["verify health/state"]
H --> C["commit release state"]2. Define inputs and outputs first
Inputs are environment, version, source directory, and optional dry-run. Output is a clean release ID on stdout; logs and diagnostics go to stderr.
3. Use explicit strictness and error handling
#!/usr/bin/env bash
set -u
set -o pipefailThe capstone uses explicit error branches instead of treating set -e as a complete error model.
4. Validate tools and environment before mutation
require_command() {
command -v "$1" >/dev/null 2>&1 || {
printf 'required command missing: %s\n' "$1" >&2
return 69
}
}
require_command tar || exit $?5. Build in a private temporary workspace
workdir=$(mktemp -d) || exit 70
cleanup() {
local status=$?
rm -rf -- "$workdir"
exit "$status"
}
trap cleanup EXIT TERM INT6. Record previous and current releases only after success
previous=""
[[ -f $STATE_DIR/current ]] &&
previous=$(cat "$STATE_DIR/current")
printf '%s\n' "$previous" > "$STATE_DIR/previous"
printf '%s\n' "$VERSION" > "$STATE_DIR/current"7. Hands-on lab: complete release orchestrator
mkdir -p "$HOME/devops-academy/bash/chapter20/lesson05/source"
cd "$HOME/devops-academy/bash/chapter20/lesson05"
printf 'hello release\n' > source/app.txt
cat > release.sh <<'EOF'
#!/usr/bin/env bash
set -u
set -o pipefail
ENVIRONMENT=${1:-}
VERSION=${2:-}
SOURCE_DIR=${3:-}
DRY_RUN=${DRY_RUN:-false}
[[ -n $ENVIRONMENT && -n $VERSION && -n $SOURCE_DIR ]] || {
printf 'usage: %s ENV VERSION SOURCE_DIR\n' "$0" >&2
exit 64
}
case $ENVIRONMENT in
dev|staging|prod) ;;
*) printf 'invalid environment=%q\n' "$ENVIRONMENT" >&2; exit 65 ;;
esac
RUN_ID="release-$(date +%s)-$$"
STATE_DIR=./state
mkdir -p -- "$STATE_DIR"
log() {
local level=$1
shift
printf 'ts=%s run_id=%s level=%s msg=%q\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$RUN_ID" "$level" "$*" >&2
}
command -v tar >/dev/null 2>&1 || {
log ERROR "tar is required"
exit 69
}
if command -v sha256sum >/dev/null 2>&1; then
checksum_tool=sha256sum
elif command -v shasum >/dev/null 2>&1; then
checksum_tool=shasum
else
log ERROR "no SHA-256 tool available"
exit 69
fi
workdir=$(mktemp -d) || exit 70
cleanup() {
local status=$?
rm -rf -- "$workdir"
exit "$status"
}
trap cleanup EXIT TERM INT
artifact="$workdir/release-${VERSION}.tar.gz"
log INFO "build start env=$ENVIRONMENT version=$VERSION"
tar -czf "$artifact" -C "$SOURCE_DIR" . || {
status=$?
log ERROR "artifact build failed status=$status"
exit "$status"
}
if [[ $checksum_tool == sha256sum ]]; then
sha256sum "$artifact" > "$artifact.sha256"
else
shasum -a 256 "$artifact" > "$artifact.sha256"
fi
if [[ $DRY_RUN == true ]]; then
log INFO "dry_run=true artifact_verified=true"
printf '%s\n' "$RUN_ID"
exit 0
fi
release_dir="releases/$VERSION"
mkdir -p -- "$release_dir"
cp -- "$artifact" "$release_dir/"
cp -- "$artifact.sha256" "$release_dir/"
previous=""
[[ -f $STATE_DIR/current ]] &&
previous=$(cat "$STATE_DIR/current")
printf '%s\n' "$previous" > "$STATE_DIR/previous"
printf '%s\n' "$VERSION" > "$STATE_DIR/current"
cat > "$release_dir/metadata.json" <<META
{
"run_id": "$RUN_ID",
"environment": "$ENVIRONMENT",
"version": "$VERSION",
"previous": "$previous"
}
META
log INFO "release complete version=$VERSION previous=$previous"
printf '%s\n' "$RUN_ID"
EOF
chmod u+x release.sh
DRY_RUN=true ./release.sh staging 1.0.0 ./source
./release.sh staging 1.0.0 ./source
./release.sh staging 1.1.0 ./source
printf '%s\n' '--- state ---'
cat state/current
cat state/previous
printf '%s\n' '--- release files ---'
find releases -maxdepth 2 -type f -printVerification checklist
8. Production hardening exercises
Extend the capstone with Bats tests, ShellCheck/shfmt gates, locking, signature verification, real remote upload, health checks, retry classification, rollback commands, checkpoint recovery, and CI artifact publication.
9. Knowledge check
Question 1. What belongs on stdout in this capstone?
Question 2. When should current-version state be updated?
Question 3. What should dry-run avoid?
Question 4. Why store the previous version?
10. Course complete
You have now covered Bash from interactive foundations through production reliability, security, CI/CD, testing, remote automation, performance, architecture, and release orchestration.
Take one real automation script and review it for inputs, quoting, idempotency, failures, logging, tests, security, portability, observability, rollback, and language fit.
11. Summary
Production-grade Bash is not about clever syntax. It is about explicit contracts, safe boundaries, repeatable tests, observable operations, recoverable failure, and knowing when another language is the better tool.
12. Further reading
- GNU Bash Reference Manual.
- ShellCheck and shfmt documentation.
- Bats-core documentation.
- Google Site Reliability Engineering and release-engineering guidance.
- OWASP command-injection, logging, and secrets guidance.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.